1. 1. 前言
  2. 2. CTF
  3. 3. EZ_Checkin
  4. 4. Give me Num
  5. 5. EzCrack

前言

Tnze yyds,距离比赛结束还有 30 秒不到的时间解出了最后一道题
逆向的那道 arm64 Linux Kernel 题有师傅解出来的话请告诉我,不甚感激。

CTF

关注公众号“云演”,有手就行

EZ_Checkin

直接打开什么都没有,通过 Dirsearch 扫出来一个 index.php~,访问查看代码:

Almighty 师傅说除了 param2 和 param3 是双 MD5 碰撞之外,其余所有的均可以数组绕过。

所以构造 Payload 如下:
http://2dd44ae4.yunyansec.com/index.php?param1=0e251288019&param2=aabg7XSs&param3=iv2Cn&param4[]=a&param5[]=b

由于平台关闭了忘记截图,所以没有 Flag 截图了

Give me Num

nc 上去,测试了一下发现:

  • 输出数字与你 Enter Num 输入的数字无关
  • 数字 2536 为一组重复

所以你可以随便输入 2536 个数,第二轮的时候把第一次给的数输进去就行。

用 pwntools 或者别的东西来写,只要满足第二轮输入的数字和第一次相同即可。这里安利一下稻子同学的 Pwntools Golang 版本:

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
package main

import (
"fmt"
"sync"
"github.com/Tnze/pwn/v2"
)

func main() {
p := pwn.Remote("129.226.4.186:7000")
for i := 0; i < 17; i++ {
p.ReadLine()
}
var table sync.Map
var num int
go func() {
var guess int
for i := 0; i < 2538; i++ {
if v, ok := table.Load(i % 2536); ok {
guess = v.(int)
} else {
guess = 0
}
_, err := p.Writer.Write([]byte(fmt.Sprintf("%d\n", guess)))
if err != nil {
fmt.Printf("Write data error: %v\n", err)
return
}
}
}()
for i := 0; ; i++ {
str, err := p.BufReader.ReadBytes('\n')
if err != nil {
fmt.Printf("ReadLine error: %v\n", err)
fmt.Println(string(str))
return
}
if _, err := fmt.Sscanf(string(str), "please enter num:%d", &num); err != nil {
fmt.Println(str, err)
break
}
if num < 500 {
fmt.Printf("[%06d] %d\n", i, num)
}
if num == 4 {
break
}
table.Store(i%2536, num)
}
fmt.Println("start interactive mode")
p.Interactive()
}

执行完毕之后,对方返回了一个 RSA 的问题:

1
2
3
4
5
6
7
cipher1=pow(m,e,n):
0x10652cdfaa84742a301254ad38685682db0723ead4a9696a391186333d2e18f1b1205385f3658c27818bcfb0bdb8b84f17b26ef267f93d08b9eef9791a7ce8ad98d4d0393b0df6696d9568caa712e5a2645ed74f758b378fd7f6f37e70248e46cd814d8b03da3287cea21a3f42a371adb565L
cipher2=pow(m+1,e,n):
0x10652cdfaa84742a301254ad38685682db0723ead4a9696a391186333d2e18f1b1205385f3660717240e2fd0a5b4a66b71329cab62c2cf4d5f3a706aeefd13c05993ce41f119bff6039327bd36bee9a01181bd366d4118ce3d8ce118dc84a3eb7e5497fa611a2c7c4811fa835faf4c1925f8L
N:
86556158206673268412999351099391537818771292689555971866425305353742222745803154706209551717854578357455003375422313884956087927511235887012764481212450066610311227919903117972157504258058310749942009463761100652057157343578534551749795505001379235681472026344861535746921918227188061252683055822118537612609
E: 3

典型的小公钥指数攻击,C 直接开 3 次幂就是明文,Payload 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
N = 86556158206673268412999351099391537818771292689555971866425305353742222745803154706209551717854578357455003375422313884956087927511235887012764481212450066610311227919903117972157504258058310749942009463761100652057157343578534551749795505001379235681472026344861535746921918227188061252683055822118537612
c2 = 0x10652cdfaa84742a301254ad38685682db0723ead4a9696a391186333d2e18f1b1205385f3660717240e2fd0a5b4a66b71329cab62c2cf4d5f3a706aeefd13c05993ce41f119bff6039327bd36bee9a01181bd366d4118ce3d8ce118dc84a3eb7e5497fa611a2c7c4811fa835faf4c1925f8
c1 = 0x10652cdfaa84742a301254ad38685682db0723ead4a9696a391186333d2e18f1b1205385f3658c27818bcfb0bdb8b84f17b26ef267f93d08b9eef9791a7ce8ad98d4d0393b0df6696d9568caa712e5a2645ed74f758b378fd7f6f37e70248e46cd814d8b03da3287cea21a3f42a371adb565
e = 3

import gmpy
import libnum

i = 0
while 1:
if (gmpy.root(c1 + i * N, 3)[1] == 1):
print(gmpy.root(c1 + i * N, 3))
break
i = i + 1

print(libnum.n2s(13040004482825757166149747768424026334639325666603913967492907164094589547731068248503236733))

得到 Flag:

EzCrack

APK 解压后,dex 文件丢进 JEB 反编译得到:

decrypt 函数都给你写好了,那就直接用呗:

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
package online.jdao;

import java.security.Key;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Main {
private static String KEY = "QWERTYUIOPWE1235QWERTYUIOPWE1235";
private static String SUCCESS_KEY = "5bCP5LyZLOW+iOS8mOengOWViu+8jOasoui/juadpeWbm+WPtuiNieWuieWFqOW3peS9nA==";

public static String calculate(String arg6) {
int v0 = arg6.length();
char[] v1 = new char[v0];
int v2;
for (v2 = 0; v2 < v0; ++v2) {
int v3 = v2 % 2 == 0 ? arg6.charAt(v2) ^ v2 + 37 : arg6.charAt(v2);
v1[v2] = ((char) v3);
}
return new String(v1);
}
public static byte[] decrypt(String arg4, byte[] arg5) throws Exception {
SecretKeySpec v0 = new SecretKeySpec(new SecretKeySpec(arg4.getBytes(), "AES").getEncoded(), "AES");
Cipher v4 = Cipher.getInstance("AES/CBC/PKCS5Padding");
v4.init(2, ((Key) v0), new IvParameterSpec("seclover12343234".getBytes()));
return v4.doFinal(arg5);
}
public static byte[] encrypt(String arg4, byte[] arg5) throws Exception {
SecretKeySpec v0 = new SecretKeySpec(new SecretKeySpec(arg4.getBytes(), "AES").getEncoded(), "AES");
Cipher v4 = Cipher.getInstance("AES/CBC/PKCS5Padding");
v4.init(1, ((Key) v0), new IvParameterSpec("seclover12343234".getBytes()));
return v4.doFinal(arg5);
}
public static void main(String[] args) throws Exception {
byte[] result = decrypt(KEY, Base64.getDecoder().decode("lLuyQzc5qO740MJhff6vF/dnvBHm0Bod2GPNRBknn/Y="));
System.out.println(calculate(new String(result)));
}
}