LITCTF-2024
LITCTF - 2024
应学长的邀请,参加了LITCTF🥰🥰
整体感觉还不错,难度中等,题目有趣。😘😘除了中途主办方设置的flag不对导致迷惑了好久之外没什么大问题
下面是我解出的题以及对应的题解。
Misc - 杂项
难度疑似有点两极分化了😢
welcome
签到题,进discord获取前半部分flag,看比赛规则获取后半部分flag。
geoguessr1
Where is this? Flag format: Use three decimal points of precision and round. The last digit of the first coordinate is ODD, and the last digit of the second coordinate is EVEN. Example: LITCTF{80.439,-23.498} (no spaces)
图寻题,请出AI🤖:https://geospy.web.app/
轻松找到目的地😁
LITCTF{24.885,121.284}
geoguessr2
Our history class recently took a walk on a field trip here. Where is this? Flag format: Use three decimal points of precision and round. The last digit of the first coordinate is EVEN, and the last digit of the second coordinate is ODD. Example:
还是用geospy
:
LITCTF{42.450,-71.233}
a little bit of tomcroppery
Once you crop an image, the cropped part is gone… right???
题目提示我们,截图过后被截掉的部分应该消失。但是部分设备存在漏洞CVE-2023-21036
,使得被截掉的部分没有被正常抹除,我们从而可以借助某些工具🔧修复未被抹除的部分。
- 漏洞介绍wiki https://en.wikipedia.org/wiki/ACropalypse
- 漏洞利用工具 https://github.com/frankthetank-music/Acropalypse-Multi-Tool
修复结果:
pokemon
LITCTF{POKEAAAG}
endless
Whoops! I deleted the file ending of this file, and now I can’t seem to figure out what type of file it was. Can you help me?
本来这道题应该是让我们修复🔧mp3文件头,但是现在的播放器以及很强大了,直接省去了这一步😂
Rev - 逆向
forgotten message
I made a cool program to show the flag, but i forgot to output it! Now that I lost the source, I can’t seem to remember the flag. Can you help me find it?
入门逆向题,丢到ida就能看到flag。
kablewy
halp WARNING: your computer might go kablewy if you try to do this one…
JavaScript逆向?其实就是base64加密了一下。😂
Burger Reviewer
Try to sneak a pizza into the burger reviewer!
原题附件: Burgers.java
一共五个部分
第一部分🍞限制了开头和结尾的字符
LITCTF{
和}
。1
2
3public static boolean bun(String s) {
return (s.substring(0, 7).equals("LITCTF{") && s.charAt(s.length()-1) == '}');
}1
2
3
4
5
6
7
8
9s = ["#"] * 42
s[0] = "L"
s[1] = "I"
s[2] = "T"
s[3] = "C"
s[4] = "T"
s[5] = "F"
s[6] = "{"
s[-1] = "}"第二部分🧀给出了下划线字符的位置
1
2
3public static boolean cheese(String s) {
return (s.charAt(13) == '_' && (int)s.charAt(17) == 95 && s.charAt(19) == '_' && s.charAt(26)+s.charAt(19) == 190 && s.charAt(29) == '_' && s.charAt(34)-5 == 90 && s.charAt(39) == '_');
}1
2
3
4
5
6
7
8
9s[13] = "_"
s[17] = chr(95)
s[19] = "_"
s[26] = chr(190 - ord(s[19]))
s[29] = "_"
s[34] = chr(90 + 5)
s[39] = "_"
print("".join(s))
# LITCTF{######_###_#_######_##_####_####_#}第三部分🥩
1
2
3
4
5
6
7
8
9
10
11
12
13
14public static boolean meat(String s) {
boolean good = true;
int m = 41;
char[] meat = {'n', 'w', 'y', 'h', 't', 'f', 'i', 'a', 'i'};
int[] dif = {4, 2, 2, 2, 1, 2, 1, 3, 3};
for (int i = 0; i < meat.length; i++) {
m -= dif[i];
if (s.charAt(m) != meat[i]) {
good = false;
break;
}
}
return good;
}1
2
3
4
5
6
7
8
9m = 41
meat = ["n", "w", "y", "h", "t", "f", "i", "a", "i"]
dif = [4, 2, 2, 2, 1, 2, 1, 3, 3]
for i in range(len(meat)):
m -= dif[i]
s[m] = meat[i]
print("".join(s))
# LITCTF{######_###_#_#i##a#_if_th#y_w#n#_#}第四部分🧂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public static boolean pizzaSauce(String s) {
boolean[] isDigit = {false, false, false, true, false, true, false, false, true, false, false, false, false, false};
for (int i = 7; i < 21; i++) {
if (Character.isDigit(s.charAt(i)) != isDigit[i - 7]) {
return false;
}
}
char[] sauce = {'b', 'p', 'u', 'b', 'r', 'n', 'r', 'c'};
int a = 7; int b = 20; int i = 0; boolean good = true;
while (a < b) {
if (s.charAt(a) != sauce[i] || s.charAt(b) != sauce[i+1]) {
good = false;
break;
}
a++; b--; i += 2;
while (!Character.isLetter(s.charAt(a))) a++;
while (!Character.isLetter(s.charAt(b))) b--;
}
return good;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22isDigit = [0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0]
for i in range(7, 21):
if isDigit[i - 7] == 1:
s[i] = str(isDigit[i - 7])
sauce = ["b", "p", "u", "b", "r", "n", "r", "c"]
a = 7
b = 20
i = 0
while a < b:
s[a] = sauce[i]
s[b] = sauce[i + 1]
a += 1
b -= 1
i += 2
while s[a] != "#":
a += 1
while s[b] != "#":
b -= 1
print("".join(s))
# LITCTF{bur1r1_c1n_b_pi##a#_if_th#y_w#n#_#}第五部分🥬
1
2
3
4
5
6
7
8public static boolean veggies(String s) {
int[] veg1 = {10, 12, 15, 22, 23, 25, 32, 36, 38, 40};
int[] veg = new int[10];
for (int i = 0; i < veg1.length; i++) {
veg[i] = Integer.parseInt(s.substring(veg1[i], veg1[i]+1));
}
return (veg[0] + veg[1] == 14 && veg[1] * veg[2] == 20 && veg[2]/veg[3]/veg[4] == 1 && veg[3] == veg[4] && veg[3] == 2 && veg[4] - veg[5] == -3 && Math.pow(veg[5], veg[6]) == 125 && veg[7] == 4 && veg[8] % veg[7] == 3 && veg[8] + veg[9] == 9 && veg[veg.length - 1] == 2);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14veg = [10, 12, 15, 22, 23, 25, 32, 36, 38, 40]
s[veg[0]] = "9"
s[veg[1]] = "5"
s[veg[2]] = "4"
s[veg[3]] = "2"
s[veg[4]] = "2"
s[veg[5]] = "5"
s[veg[6]] = "3"
s[veg[7]] = "4"
s[veg[8]] = "7"
s[veg[9]] = "2"
print("".join(s))
# LITCTF{bur9r5_c4n_b_pi22a5_if_th3y_w4n7_2}
revsite1
wasm逆向
这题由于代码太长,我们采用动态调试的方法。
首先找到flag检查的相关函数。
按钮事件。
对应函数,调用了wasm中的check_flag
函数。
来到对应函数,一路往下翻,看到了一个$strcmp
,狂喜。
在strcmp
函数内打下断点,查看相关变量,经测试得到$var3
是对应的flag字符。
往下找到了loop
结构,估计就是循环遍历字符来比较了。
在此处设置一个日志点,捕获下一个flag字符。
最后,循环输入新蹦出的字符并再次检查即可拼接出完整的flag。
Web - 网站
anti-inspect
can you find the answer? WARNING: do not open the link your computer will not enjoy it much.
jwt-1
I just made a website. Since cookies seem to be a thing of the old days, I updated my authentication! With these modern web technologies, I will never have to deal with sessions again.
没有签名校验的jwt,使用jwt.io修改身份即可。😋
jwt-2
its like jwt-1 but this one is harder
源码泄露,给了对应的key。😋
traversed
I made this website! you can’t see anything else though… right??
拿dirsearch
扫了一下,发现有目录穿越漏洞。😋
flag在 /../flag.txt
kirbytime
Welcome to Kirby’s Website.
附件: kirbytime.zip
审查了一下代码,发现有人为设置的侧信道攻击点。😋
祭出万能的python
1 |
|
如果密码正确,就会延时一秒,人眼判断一下异常的时间,最后得到密码 kBySlaY
scrainbow
Oh no! someone dropped my perfect gradient and it shattered into 10000 pieces! I can’t figure out how to put it back together anymore, it never looks quite right. Can you help me fix it?
压轴出现!这道题应该是这个比赛中我做出最麻烦的一道题了。🧐
题目给了一个100*100
的颜色矩阵,定义了一种操作交换
,既交换两个颜色色块,交换的操作表示为 [ FromPos, ToPos ]
。要求给出操作列表,使得这个颜色矩阵变成从左上角到右下角为 红色-彩色-红色
。
吐槽一下,这道题怎么给我一种在隔壁做算法题的感觉……😕
附上几张图:
数据来源 | 最终的图片 |
---|---|
思路如下:
获取数据集:
- 使用
requests.get
方法从指定 URL 获取颜色数据集,并将其解析为 JSON 格式的列表。
- 使用
颜色转换和距离计算:
- 定义了一个函数
hex_to_rgb
,用于将十六进制颜色代码转换为 RGB 元组。 - 定义了另一个函数
calc_distance
,用于计算两个 RGB 颜色之间的欧几里得距离。
- 定义了一个函数
统计颜色出现次数并排序:
- 使用一个字典
color_dict
来统计每种颜色在数据集中出现的次数。 - 将字典按颜色出现次数进行排序,得到一个列表
color_dict
。
- 使用一个字典
生成期望的颜色排序:
- 初始化一个
expect_color
列表,用于存储期望的颜色排序。 - 选择出现次数第二多的颜色作为起始颜色,并将其添加到
expect_color
列表中。 - 复制并删除已选择的颜色,避免重复使用。
- 通过计算当前颜色与其他颜色之间的距离,选择距离最近的颜色依次添加到
expect_color
列表中。 - 逆序添加剩余的颜色到
expect_color
列表中,确保所有颜色都被包含。
- 初始化一个
生成目标列表:
- 初始化一个
target_list
列表,用于存储最终的目标颜色排列。 - 按照一定的规则(例如每次移动一个位置)将
expect_color
列表中的颜色添加到target_list
列表中。
- 初始化一个
生成交换操作列表:
- 定义一个函数
find_swap_operations
,用于生成将原始颜色列表转换为目标颜色列表所需的交换操作。 - 通过遍历原始列表和目标列表,找到需要交换的位置,并记录交换操作。
- 定义一个函数
提交交换操作列表:
- 使用
requests.post
方法将生成的交换操作列表提交到指定的服务器 URL。 - 打印服务器返回的响应,确认操作是否成功。
- 使用
1 |
|
总结
队友太强了,把密码ak了。🥳🥳
but我们队没人做pwn,所以最后没有拿到什么好名次。😭
整体下来这个比赛还是挺开心的🤭,要说收获也挺多,拿了两个一血(虽然很水),第一次尝试wasm,第一次在ctf写算法题…