HGAME 2024网络攻防大赛—week2部分题目题解
reverse ezcpp IDA打开
是一个展开的tea
并且就前几个字节在加密,直接上tea经典解密脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <stdio.h> #include <stdint.h> #include <stdlib.h> void decrypt (uint32_t * v, uint32_t * k) { uint32_t delta = 0xdeadbeef ; uint32_t v0 = v[0 ] , v1 = v[1 ] , sum = delta * 32 , i; uint32_t k0 = k[0 ], k1 = k[1 ], k2 = k[2 ], k3 = k[3 ]; for (i = 0 ; i < 32 ; i++) { v1 -= (v0 + sum) ^ (k2 + (v0 << 4 )) ^ (k3 + (v0 << 5 )); v0 -= (v1 + sum) ^ (k0 + (v1 << 4 )) ^ (k1 + (v1 << 5 )); sum -= delta; } v[0 ] = v0; v[1 ] = v1; } int main () { uint32_t j; int8_t v[33 ] = { 136 , 106 , 176 , 201 , 173 , 241 , 51 , 51 , 148 , 116 , 181 , 105 , 115 , 95 , 48 , 98 , 74 , 51 , 99 , 84 , 95 , 48 , 114 , 49 , 101 , 110 , 84 , 101 , 68 , 63 , 33 , 125 }; uint32_t k[4 ] = { 1234 ,2341 ,3412 ,4123 }; decrypt((uint32_t *)&v[3 ] ,k); decrypt((uint32_t *)&v[2 ] ,k); decrypt((uint32_t *)&v[1 ] ,k); decrypt((uint32_t *)&v[0 ] ,k); printf (v); system("pause" ); return 0 ; }
arithmetic IDA打开数据很少,应该是有壳
查壳为upx,但直接使用脚本脱不了,拉近010查看字节码
得知被改特征
将三处特征改回后可以使用脚本脱,脱壳后IDA打开
IDA打开是一个三角形最大路径向下算法,三角形在附近中给出文件
flag也是最大路径的md5加密,那么我们需要得到路径
一般快速计算最大向下路径的算法都是自底向上的,但是这里计算路径需要自上向下
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <stdio.h> #include <stdlib.h> int max (int a, int b) { return a > b ? a : b; } int fun (int n, int i, int j, int **a, int **op) { if (i == n) { return 0 ; } if (op[i][j] != 0 ) return op[i][j]; op[i][j] = a[i][j] + max(fun(n, i + 1 , j, a, op), fun(n, i + 1 , j + 1 , a, op)); return op[i][j]; } void Find_path (int **op, int **a, int n) { int j = 0 ; printf ("路径如下:\n" ); for (int i = 1 ; i < n; i++) { int node = op[i - 1 ][j] - a[i - 1 ][j]; if (node == op[i][j + 1 ]){ printf ("%d" , 2 ); j++; } else printf ("%d" , 1 ); } } int main () { int n; printf ("Enter the size of the triangle: " ); scanf ("%d" , &n); int **data = (int **)malloc (sizeof (int *) * n); for (int i = 0 ; i < n; i++) { data[i] = (int *)malloc (sizeof (int ) * n); } for (int i = 0 ; i < n; i++) { for (int j = 0 ; j <= i; j++) { data[i][j] = 0 ; } } FILE *file = fopen("out.txt" , "r" ); if (file == NULL ) { fprintf (stderr , "Error opening the file.\n" ); return 1 ; } for (int i = 0 ; i < n; i++) { for (int j = 0 ; j <= i; j++) { fscanf (file, "%d" , &data[i][j]); } } fclose(file); int **op = (int **)malloc (sizeof (int *) * n); for (int i = 0 ; i < n; i++) { op[i] = (int *)malloc (sizeof (int ) * n); } for (int x = 0 ; x < n; x++) { for (int y = 0 ; y < n; y++) op[x][y] = 0 ; } int result = fun(n, 0 , 0 , data, op); printf ("最大路径是:%d\n" , result); Find_path(op, data, n); system("pause" ); return 0 ; }
最后得到的路径md5加密就是flag
babyAndroid apk文件jadx打开
有两个函数校验username和password,其中check2是native层的方法,那么先看check1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class Check1 { private byte [] S = new byte [256 ]; private int i; private int j; public Check1 (byte [] bArr) { for (int i = 0 ; i < 256 ; i++) { this .S[i] = (byte ) i; } int i2 = 0 ; for (int i3 = 0 ; i3 < 256 ; i3++) { byte [] bArr2 = this .S; i2 = (i2 + bArr2[i3] + bArr[i3 % bArr.length]) & 255 ; swap(bArr2, i3, i2); } this .i = 0 ; this .j = 0 ; } private void swap (byte [] bArr, int i, int i2) { byte b = bArr[i]; bArr[i] = bArr[i2]; bArr[i2] = b; } public byte [] encrypt(byte [] bArr) { byte [] bArr2 = new byte [bArr.length]; for (int i = 0 ; i < bArr.length; i++) { int i2 = (this .i + 1 ) & 255 ; this .i = i2; int i3 = this .j; byte [] bArr3 = this .S; int i4 = (i3 + bArr3[i2]) & 255 ; this .j = i4; swap(bArr3, i2, i4); byte [] bArr4 = this .S; bArr2[i] = (byte ) (bArr4[(bArr4[this .i] + bArr4[this .j]) & 255 ] ^ bArr[i]); } return bArr2; } public boolean check (byte [] bArr) { return Arrays.equals(new byte []{-75 , 80 , 80 , 48 , -88 , 75 , 103 , 45 , -91 , 89 , -60 , 91 , -54 , 5 , 6 , -72 }, encrypt(bArr)); } }
一个小改过的rc4,找到key对密文重新加密一遍就是解密
这里的key在资源文件中
使用apktools提取文件,在res/value/string.xml找到key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package package0;import java.util.Arrays;public class Check1 { private byte [] S = new byte [256 ]; private int i; private int j; public Check1 (byte [] bArr) { for (int i = 0 ; i < 256 ; i++) { this .S[i] = (byte ) i; } int i2 = 0 ; for (int i3 = 0 ; i3 < 256 ; i3++) { byte [] bArr2 = this .S; i2 = (i2 + bArr2[i3] + bArr[i3 % bArr.length]) & 255 ; swap(bArr2, i3, i2); } this .i = 0 ; this .j = 0 ; } private void swap (byte [] bArr, int i, int i2) { byte b = bArr[i]; bArr[i] = bArr[i2]; bArr[i2] = b; } public byte [] encrypt(byte [] bArr) { byte [] bArr2 = new byte [bArr.length]; for (int i = 0 ; i < bArr.length; i++) { int i2 = (this .i + 1 ) & 255 ; this .i = i2; int i3 = this .j; byte [] bArr3 = this .S; int i4 = (i3 + bArr3[i2]) & 255 ; this .j = i4; swap(bArr3, i2, i4); byte [] bArr4 = this .S; bArr2[i] = (byte ) (bArr4[(bArr4[this .i] + bArr4[this .j]) & 255 ] ^ bArr[i]); } return bArr2; } public void check (byte [] bArr) { System.out.println(Arrays.toString(encrypt(bArr))); } public static void main (String[] args) { String key = "3e1fel" ; byte [] keyBytes = key.getBytes(); Check1 rc4 = new Check1 (keyBytes); byte [] encryptedData = {-75 , 80 , 80 , 48 , -88 , 75 , 103 , 45 , -91 , 89 , -60 , 91 , -54 , 5 , 6 , -72 }; byte [] decryptedData = rc4.encrypt(encryptedData); System.out.println(Arrays.toString(decryptedData)); } } #G>IkH<aHu5FE3GSV
回到main方法分析check2,是native层的方法,使用IDA打开apktools分解出来的lib中的.so文件
应该是aes256加密,那么因为check2传入了username和password
猜测username就是key,cyberchef解密
babyre 虚拟机逆向,IDA打开分析
首先查看sub_5630D3194708()
函数
这里是输入flag,并且在flag后又增加一个值为249的内容
主函数中signal(8, handler);
函数,是有浮点异常时执行handler
handler函数中,是对刚刚flag最后追加的249执行+1的操作,也就是说抛出异常时249就会+1
那么动调起来看看有没有异常
果然有异常,那么flag后追加的内容就是250
回到main函数继续分析
四个线程对flag进行加密,最后sub_5630D3194803()
校验flag
这里先对几个函数进行解释
sem_init
:对由sem指定的信号量进行初始化
sem
:为指向信号量结构的一个指针
sem_post
:用来增加信号量的值
sem_wait
:用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一
那么现在先进入第一个加密函数进行分析
第一个加密函数先用sem_wait
函数对sem进行-1然后执行加密
加密结束后使用sem_post
来增加下一个加密函数的信号量,即进行下一个函数的加密
那么依次加密,我们看到最后一个加密函数
最后一个加密函数又使用sem_post
增加第一个加密函数的信号量
因此构成循环,逐个对flag进行加密,总共32字节
其中每个加密函数中&dword_5630D31970A0
是一个key数组,并且真正的key需要动调才可以拿到
在主函数中有对key的异或加密,因此需要对key进行异或后才是真正的key
从前往后加密,从后往前解密
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <stdlib.h> char key[] = {119 , 116 , 120 , 102 , 101 , 105 };int main () { int enc[33 ] = {12052 , 78 , 20467 , 109 , 13016 , 109 , 27467 , -110 , 9807 , 91 , 21243 , -100 , 11121 , 20 , 10863 , -107 , 10490 , 29 , 10633 , -101 , 10420 , 78 , 17670 , -38 , 6011 , -4 , 16590 , 125 , 10723 , 15 , 7953 , 255 , 250 }; for (int i = 28 ; i >= 0 ; i -= 4 ) { enc[i + 3 ] = enc[i + 3 ] ^ (enc[i + 4 ] - key[(i + 4 ) % 6 ]); enc[i + 2 ] = enc[i + 2 ] / (enc[i + 3 ] + key[(i + 3 ) % 6 ]); enc[i + 1 ] = enc[i + 1 ] + (enc[i + 2 ] ^ key[(i + 2 ) % 6 ]); enc[i + 0 ] = enc[i + 0 ] - (enc[i + 1 ] * key[(i + 1 ) % 6 ]); } for (int i = 0 ; i < 32 ; i++) { printf ("%c" , enc[i]); } system("pause" ); return 0 ; }
crypto midRSA midRSA.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from Crypto.Util.number import *from secret import flagdef padding (flag ): return flag+b'\xff' *(64 -len (flag)) flag=padding(flag) m=bytes_to_long(flag) p=getPrime(512 ) q=getPrime(512 ) e=3 n=p*q c=pow (m,e,n) m0=m>>208 print (f'n={n} ' )print (f'c={c} ' )print (f'm0={m0} ' )""" n=120838778421252867808799302603972821425274682456261749029016472234934876266617266346399909705742862458970575637664059189613618956880430078774892479256301209695323302787221508556481196281420676074116272495278097275927604857336484564777404497914572606299810384987412594844071935546690819906920254004045391585427 c=118961547254465282603128910126369011072248057317653811110746611348016137361383017921465395766977129601435508590006599755740818071303929227578504412967513468921191689357367045286190040251695094706564443721393216185563727951256414649625597950957960429709583109707961019498084511008637686004730015209939219983527 m0=13292147408567087351580732082961640130543313742210409432471625281702327748963274496942276607 """
m高位泄露,直接解了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from Crypto.Util.number import long_to_bytesn=120838778421252867808799302603972821425274682456261749029016472234934876266617266346399909705742862458970575637664059189613618956880430078774892479256301209695323302787221508556481196281420676074116272495278097275927604857336484564777404497914572606299810384987412594844071935546690819906920254004045391585427 c=118961547254465282603128910126369011072248057317653811110746611348016137361383017921465395766977129601435508590006599755740818071303929227578504412967513468921191689357367045286190040251695094706564443721393216185563727951256414649625597950957960429709583109707961019498084511008637686004730015209939219983527 m_high=13292147408567087351580732082961640130543313742210409432471625281702327748963274496942276607 m_high <<= 208 e = 3 R.<x> = PolynomialRing(Zmod(n)) m = m_high + x f = m^e - c f = f.monic() x = f.small_roots(X = 2 ^208 ,beta = 0.4 ) if x: m = m_high + x[0 ] print (long_to_bytes(int (m)))
midRSA revenge attachment.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from Crypto.Util.number import *from secret import flagm=bytes_to_long(flag) p=getPrime(1024 ) q=getPrime(1024 ) e=5 n=p*q c=pow (m,e,n) m0=m>>128 print (f'n={n} ' )print (f'c={c} ' )print (f'm0={m0} ' )""" n=27814334728135671995890378154778822687713875269624843122353458059697288888640572922486287556431241786461159513236128914176680497775619694684903498070577307810263677280294114135929708745988406963307279767028969515305895207028282193547356414827419008393701158467818535109517213088920890236300281646288761697842280633285355376389468360033584102258243058885174812018295460196515483819254913183079496947309574392848378504246991546781252139861876509894476420525317251695953355755164789878602945615879965709871975770823484418665634050103852564819575756950047691205355599004786541600213204423145854859214897431430282333052121 c=456221314115867088638207203034494636244706611111621723577848729096069230067958132663018625661447131501758684502639383208332844681939698124459188571813527149772292464139530736717619741704945926075632064072125361516435631121845753186559297993355270779818057702973783391589851159114029310296551701456748698914231344835187917559305440269560613326893204748127999254902102919605370363889581136724164096879573173870280806620454087466970358998654736755257023225078147018537101 m0=9999900281003357773420310681169330823266532533803905637 """
同上,exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from Crypto.Util.number import long_to_bytesn=27814334728135671995890378154778822687713875269624843122353458059697288888640572922486287556431241786461159513236128914176680497775619694684903498070577307810263677280294114135929708745988406963307279767028969515305895207028282193547356414827419008393701158467818535109517213088920890236300281646288761697842280633285355376389468360033584102258243058885174812018295460196515483819254913183079496947309574392848378504246991546781252139861876509894476420525317251695953355755164789878602945615879965709871975770823484418665634050103852564819575756950047691205355599004786541600213204423145854859214897431430282333052121 c=456221314115867088638207203034494636244706611111621723577848729096069230067958132663018625661447131501758684502639383208332844681939698124459188571813527149772292464139530736717619741704945926075632064072125361516435631121845753186559297993355270779818057702973783391589851159114029310296551701456748698914231344835187917559305440269560613326893204748127999254902102919605370363889581136724164096879573173870280806620454087466970358998654736755257023225078147018537101 m_high=9999900281003357773420310681169330823266532533803905637 m_high <<= 128 e = 5 R.<x> = PolynomialRing(Zmod(n)) m = m_high + x f = m^e - c f = f.monic() x = f.small_roots(X = 2 ^208 ,beta = 0.4 ) if x: m = m_high + x[0 ] print (long_to_bytes(int (m)))
backpack attachment.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from Crypto.Util.number import *import randomfrom secret import flaga=[getPrime(32 ) for _ in range (20 )] p=random.getrandbits(32 ) assert len (bin (p)[2 :])==32 bag=0 for i in a: temp=p%2 bag+=temp*i p=p>>1 enc=bytes_to_long(flag)^p print (f'enc={enc} ' )print (f'a={a} ' )print (f'bag={bag} ' )""" enc=871114172567853490297478570113449366988793760172844644007566824913350088148162949968812541218339 a=[3245882327, 3130355629, 2432460301, 3249504299, 3762436129, 3056281051, 3484499099, 2830291609, 3349739489, 2847095593, 3532332619, 2406839203, 4056647633, 3204059951, 3795219419, 3240880339, 2668368499, 4227862747, 2939444527, 3375243559] bag=45893025064 """
背包密码
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import libnumenc = 871114172567853490297478570113449366988793760172844644007566824913350088148162949968812541218339 M = [3245882327 , 3130355629 , 2432460301 , 3249504299 , 3762436129 , 3056281051 , 3484499099 , 2830291609 , 3349739489 , 2847095593 , 3532332619 , 2406839203 , 4056647633 , 3204059951 , 3795219419 , 3240880339 , 2668368499 , 4227862747 , 2939444527 , 3375243559 ] S = 45893025064 n = len (M) Ge = Matrix.identity(n) last_row = [0 for x in range (n)] Ge_last_row = Matrix(ZZ, 1 , len (last_row), last_row) last_col = M[:] last_col.append(S) Ge_last_col = Matrix(ZZ, len (last_col), 1 , last_col) Ge = Ge.stack(Ge_last_row) Ge = Ge.augment(Ge_last_col) X = Ge.LLL()[-1 ] X = X[:-1 ] p = "" for i in X: if abs (i) == 1 : p += "1" if abs (i) == 0 : p += "0" print (p)m = int (p,2 ) ^^ enc print (m)flag = bytes .fromhex(hex (int (m))[2 :]) print (flag)
非预期:
1 2 3 from Crypto.Util.number import long_to_bytesenc=871114172567853490297478570113449366988793760172844644007566824913350088148162949968812541218339 print (long_to_bytes(enc))
babyRSA attachment.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from Crypto.Util.number import *from secret import flag,em=bytes_to_long(flag) p=getPrime(64 ) q=getPrime(256 ) n=p**4 *q k=getPrime(16 ) gift=pow (e+114514 +p**k,0x10001 ,p) c=pow (m,e,n) print (f'p={p} ' )print (f'q={q} ' )print (f'c={c} ' )print (f'gift={gift} ' )""" p=14213355454944773291 q=61843562051620700386348551175371930486064978441159200765618339743764001033297 c=105002138722466946495936638656038214000043475751639025085255113965088749272461906892586616250264922348192496597986452786281151156436229574065193965422841 gift=9751789326354522940 """
使用nth_root解
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from Crypto.Util.number import *import gmpy2p = 14213355454944773291 q = 61843562051620700386348551175371930486064978441159200765618339743764001033297 c = 105002138722466946495936638656038214000043475751639025085255113965088749272461906892586616250264922348192496597986452786281151156436229574065193965422841 gift = 9751789326354522940 n = p**4 *q d = gmpy2.invert(65537 ,p-1 ) temp = pow (gift,d,p) e = temp - 114514 res = Zmod(n)(c).nth_root(e, all =True ) for m in res: flag = long_to_bytes(int (m)) if b"hgame" in flag: print (flag) break
misc ek1ng_want_girlfriend wireshark打开
追踪http,后导出字节流
得到flag
ezWord 010打开为PK开头,是一个压缩包
改zip后缀后解压找到关键文件
根据hint是盲水印,解出来后得到压缩包密码(这里第一次做盲水印题,对老脚本里的随机函数需要进行修改,卡了好久)
将脚本里的random中的参数给删除
得到key
解开压缩包后难道一堆乱七八糟的英文,上网查出来是fake加密
解密后得到一堆中文乱码
籱籰籪籶籮粄簹籴籨粂籸籾籨籼簹籵籿籮籨籪籵簺籨籽籱簼籨籼籮籬类簼籽粆
刚开始有想到Unicode,但是没解出来
后面根据出题人提示得知rot8000(这里看见中文的字符应该就要考虑是不是Unicode偏移,密文的Unicode偏移多少会到hgame),解出flag