IRON CTF 2024 Writeup
Warmup - Welcome
Welcome to IRONCTF 2024 join our discord server and read the rules to claim your reward.
Discordのrulesチャンネルの投稿にflagが記載されています。
正答:ironCTF{W3lc0m3_t0_ir0nCTF_2024}
Warmup - Math Gone Wrong
Bob: Computers are excellent in maths
CTFplayer: No!!
Bob: prove me!!
Author:AbdulHaq
nc misc.1nf1n1ty.team 30011
問題文末のコマンドを入力してサーバに接続してみると、数を2つ入力するよう求められます。
まずは適当な数(ここでは1と2)を入力してみます。
$ nc misc.1nf1n1ty.team 30011
Enter frist number (n1) > 1
Enter second number (n2) > 2
n1*10+n2*10 != (n1+n2)*10
above condition is false so no flag
$
どうやらn1*10+n2*10 != (n1+n2)*10
が成り立つようなn1
とn2
を入力しないとflagを得られないようです。
数学の世界では常にn1*10+n2*10 == (n1+n2)*10
となってしまいますが、問題文冒頭にある通り今はコンピュータの世界です。
2進数では10進数の小数を正確に表せないことがある、つまり誤差が出ることがある性質を利用して、0.1
と0.2
を入力してみます。
$ nc misc.1nf1n1ty.team 30011
Enter frist number (n1) > 0.1
Enter second number (n2) > 0.2
b'ironCTF{s1mpl3_r3m4ind3r_70_b3w4r3_0f_fl047ing_p0in7_3rr0r}'
$
正答:ironCTF{s1mpl3_r3m4ind3r_70_b3w4r3_0f_fl047ing_p0in7_3rr0r}
Warmup - Introspection
Know your inner self and get started with Pwn.
Author:Vigneswar
nc pwn.1nf1n1ty.team 31698
問題にはintrospection.c
とコンパイル後のファイルと思われるintrospection
が添付されていました。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
printf("\033[32m\"Introspection is the key to unlocking your fullest potential; knowing yourself is the first step.\"\033[0m\n\n");
printf(" - ChatGPT\n");
printf("Have you thought about what you really wanted in life?\n");
char flag[50];
FILE *file = fopen("flag.txt", "r");
if (file == NULL)
{
printf("Error! flag.txt not found!");
exit(1);
}
fread(flag, 1, 50, file);
char buf[1008];
printf(">> ");
read(0, buf, 1008);
printf("I wish for you that you get %s", buf);
}
introspection.c
の以下の部分では、50バイトのバッファflag
を作成し、そこにflag.txt
の内容を読み込みます。
char flag[50];
FILE *file = fopen("flag.txt", "r");
if (file == NULL)
{
printf("Error! flag.txt not found!");
exit(1);
}
fread(flag, 1, 50, file);
次に、1008バイトのバッファbuf
を作成し、そこに標準入力の内容を読み込みます。
char buf[1008];
printf(">> ");
read(0, buf, 1008);
最後に、buf
の内容を標準出力します。
printf("I wish for you that you get %s", buf);
ここで、標準入力にて1008バイト以上の文字列を入力すると、%s
はバッファbuf
の領域を超えてnull終端文字\0
が現れるまで読み込もうとします。
つまり、標準入力にて1008バイト以上の文字列を入力すれば、バッファbuf
の直後にあるバッファflag
の内容が表示されます。
$ python -c 'print("A" * 1008)' | nc pwn.1nf1n1ty.team 31698
"Introspection is the key to unlocking your fullest potential; knowing yourself is the first step."
- ChatGPT
Have you thought about what you really wanted in life?
>> I wish for you that you get AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAironCTF{W0w!_Y0u_Just_OverWrite_the_Nul1!}
/D
$
正答:ironCTF{W0w!_Y0u_Just_OverWrite_the_Nul1!}
Warmup - Same but Different
In the world of cryptography, not everything is what it seems. You've stumbled upon a cryptic note that contains 79054025255fb1a26e4bc422aef54eb4
There are two hex strings related to this. What sets them apart?
Flag format: ironCTF{hexstring1_hexstring2}
Author:p3rplex3d
79054025255fb1a26e4bc422aef54eb4
は32文字から成る16進数なので、MD5ハッシュ値であると考えられます。
MD5は衝突するハッシュが発見されている脆弱なアルゴリズムです。
実際に79054025255fb1a26e4bc422aef54eb4
をGoogle検索してみると、以下の異なる2つの16進数のMD5ハッシュ値が79054025255fb1a26e4bc422aef54eb4
になることが分かります。
d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70
d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70
念のため、この2つの16進数のMD5ハッシュ値が一致することをCyberChefで確認します。
この2つの16進数の差分がflagとなります。
str_a = "d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70"
str_b = "d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70"
l = len(str_a)
diff_a = "ironCTF{"
diff_b = "_"
for a, b in zip(str_a, str_b):
if a != b:
diff_a += a
diff_b += b
print(diff_a + diff_b + "}")
$ python diff.py
ironCTF{87fba2_0f732a}
$
正答:ironCTF{87fba2_0f732a}
Crypto - Algebra Exam
My Algebra professor enjoys giving us difficult tests. I definitely need your help on this one.
Author:
p3rplex3d
Wrap the answer in uppercase with ironCTF{}
問題にはquestions.txtが添付されており、内容は以下の通りです。
x=-3{-6<y<-4}
x=-1{-6<y<-4}
-x-7{-3<x<-2}
x-3{-2<x<-1}
2x+10{-5<=x<=-4}
-2x-6{-4<=x<=-3}
y=1{-4.5<=x<=-3.5}
y=-7{2<x<4}
x=3{-9<y<-7}
x=-1{-3<=y<=-1}
x=0{-3<=y<=-1}
y=-2{-1<=x<=0}
y=-1{1<=x<=2}
y=-3{1<=x<=2}
x=1.5{-3<=y<=-1}
(x-0.5)^2+(y+7.5)^2=.25{y>-7.5}
(x-0.5)^2+(y+7.5)^2=.25{x<.5}
(x-0.5)^2+(y+8.5)^2=.25{x>.5}
y=-1{-5<=x<=-4}
y=-2{-5<=x<=-4.25}
x=-5{-3<=y<=-1}
x=5{-8.5<y<-7}
x=6{-8.5<y<-7}
(x-5.5)^2+(y+8.5)^2=.25{y<-8.5}
x=0{-6<y<-4}
(-4/3)x-4 {0<x<1.5}
x=1.5{-6<y<-4}
この27個の数式をグラフ化してみます。
import matplotlib.pyplot
import numpy
figure, axes = matplotlib.pyplot.subplots()
# x=-3{-6<y<-4}
y_vals = numpy.linspace(-6, -4, 100)
axes.plot([-3]*len(y_vals), y_vals)
# x=-1{-6<y<-4}
y_vals = numpy.linspace(-6, -4, 100)
axes.plot([-1]*len(y_vals), y_vals)
# -x-7{-3<x<-2}
x_vals = numpy.linspace(-3, -2, 100)
y_vals = -x_vals - 7
axes.plot(x_vals, y_vals)
# x-3{-2<x<-1}
x_vals = numpy.linspace(-2, -1, 100)
y_vals = x_vals - 3
axes.plot(x_vals, y_vals)
# 2x+10{-5<=x<=-4}
x_vals = numpy.linspace(-5, -4, 100)
y_vals = 2 * x_vals + 10
axes.plot(x_vals, y_vals)
# -2x-6{-4<=x<=-3}
x_vals = numpy.linspace(-4, -3, 100)
y_vals = -2 * x_vals - 6
axes.plot(x_vals, y_vals)
# y=1{-4.5<=x<=-3.5}
x_vals = numpy.linspace(-4.5, -3.5, 100)
axes.plot(x_vals, [1]*len(x_vals))
# y=-7{2<x<4}
x_vals = numpy.linspace(2, 4, 100)
axes.plot(x_vals, [-7]*len(x_vals))
# x=3{-9<y<-7}
y_vals = numpy.linspace(-9, -7, 100)
axes.plot([3]*len(y_vals), y_vals)
# x=-1{-3<=y<=-1}
y_vals = numpy.linspace(-3, -1, 100)
axes.plot([-1]*len(y_vals), y_vals)
# x=0{-3<=y<=-1}
y_vals = numpy.linspace(-3, -1, 100)
axes.plot([0]*len(y_vals), y_vals)
# y=-2{-1<=x<=0}
x_vals = numpy.linspace(-1, 0, 100)
axes.plot(x_vals, [-2]*len(x_vals))
# y=-1{1<=x<=2}
x_vals = numpy.linspace(1, 2, 100)
axes.plot(x_vals, [-1]*len(x_vals))
# y=-3{1<=x<=2}
x_vals = numpy.linspace(1, 2, 100)
axes.plot(x_vals, [-3]*len(x_vals))
# x=1.5{-3<=y<=-1}
y_vals = numpy.linspace(-3, -1, 100)
axes.plot([1.5]*len(y_vals), y_vals)
# (x-0.5)^2+(y+7.5)^2=.25{y>-7.5}
theta = numpy.linspace(0, 2 * numpy.pi, 100)
x_circle = 0.5 + 0.5 * numpy.cos(theta)
y_circle = -7.5 + 0.5 * numpy.sin(theta)
axes.plot(x_circle[y_circle > -7.5], y_circle[y_circle > -7.5])
# (x-0.5)^2+(y+7.5)^2=.25{x<.5}
theta = numpy.linspace(0, 2 * numpy.pi, 100)
x_circle = 0.5 + 0.5 * numpy.cos(theta)
y_circle = -7.5 + 0.5 * numpy.sin(theta)
axes.plot(x_circle[x_circle < 0.5], y_circle[x_circle < 0.5])
# (x-0.5)^2+(y+8.5)^2=.25{x>.5}
theta = numpy.linspace(-numpy.pi / 2, numpy.pi / 2, 100)
x_circle = 0.5 + 0.5 * numpy.cos(theta)
y_circle = -8.5 + 0.5 * numpy.sin(theta)
axes.plot(x_circle, y_circle)
# y=-1{-5<=x<=-4}
x_vals = numpy.linspace(-5, -4, 100)
axes.plot(x_vals, [-1]*len(x_vals))
# y=-2{-5<=x<=-4.25}
x_vals = numpy.linspace(-5, -4.25, 100)
axes.plot(x_vals, [-2]*len(x_vals))
# x=-5{-3<=y<=-1}
y_vals = numpy.linspace(-3, -1, 100)
axes.plot([-5]*len(y_vals), y_vals)
# x=5{-8.5<y<-7}
y_vals = numpy.linspace(-8.5, -7, 100)
axes.plot([5]*len(y_vals), y_vals)
# x=6{-8.5<y<-7}
y_vals = numpy.linspace(-8.5, -7, 100)
axes.plot([6]*len(y_vals), y_vals)
# (x-5.5)^2+(y+8.5)^2=.25{y<-8.5}
theta = numpy.linspace(0, 2 * numpy.pi, 100)
x_circle = 5.5 + 0.5 * numpy.cos(theta)
y_circle = -8.5 + 0.5 * numpy.sin(theta)
axes.plot(x_circle[y_circle < -8.5], y_circle[y_circle < -8.5])
# x=0{-6<y<-4}
y_vals = numpy.linspace(-6, -4, 100)
axes.plot([0]*len(y_vals), y_vals)
# (-4/3)x-4 {0<x<1.5}
x_vals = numpy.linspace(0, 1.5, 100)
y_vals = (-4/3) * x_vals - 4
axes.plot(x_vals, y_vals)
# x=1.5{-6<y<-4}
y_vals = numpy.linspace(-6, -4, 100)
axes.plot([1.5]*len(y_vals), y_vals)
axes.set_xlabel('x')
axes.set_ylabel('y')
matplotlib.pyplot.show()
$ py graph.py
$
描画された文字を並べ替えるとflagになります。
正答:ironCTF{MATHISFUN}
Crypto - Dantzig's Puzzle
Dantzig was on a trip with his friends and one day, they decided to play a game or atleast, it's easier version. He would think of a string and the others have to find it based on the clues he gives them.
- The knapsack list contains 8 numbers, with 1 and 2 as the first two numbers and the subsequent numbers are formed by adding one to the sum of all the numbers before.
- The value of m=257 and n is something less than 257
- The encrypted string is - [538, 619, 944, 831, 360, 531, 468, 971, 635, 593, 655, 425, 1068, 530, 1068, 360, 706, 1068, 299, 619, 670, 1068, 891, 425, 670, 1068, 371, 670, 732, 531, 1068, 484, 372, 635, 371, 372, 237, 237, 1007]
Can you find the string Dantzig was thinking of?
Flag Format: ironCTF{this_is_fake}
Author:p3rplex3d
暗号の問題でknapsackと言えば、Merkle-Hellmanナップサック暗号です。
Merkle-Hellmanナップサック暗号における暗号化・復号の手順は以下の通りです。
前提
- 平文:
※(p_1,\, \cdots,\, p_l) は0または1p_i\, (i = 1,\, \cdots,\, l) - 超増加列(秘密鍵)[1]:
(w_1,\, \cdots,\, w_l) - 整数(秘密鍵):
※m \Sigma_{i=1}^{l}w_i < m - 整数(秘密鍵):
※n はn と互いに素m - 公開鍵:
※(a_1,\, \cdots,\, a_l) a_i = nw_i \pmod{m}\, (i = 1,\, \cdots,\, l)
暗号化の手順
-
を計算します。c = \Sigma_{i=1}^{l}a_ip_i\, (i = 1,\, \cdots,\, l)
復号の手順
-
を計算します。c' = cn^{-1} \pmod{m}\, (i = 1,\, \cdots,\, l) -
となるため、これを満たすc' = \Sigma_{i=1}^{l}w_ip_i を計算します。(p_1,\, \cdots,\, p_l)
Merkle-Hellmanナップサック暗号では
- 数列:
※(p_1,\, \cdots,\, p_l) は0または1p_i\, (i = 1,\, \cdots,\, l) - 超増加列:
(w_1,\, \cdots,\, w_l) - 非超増加列:
(a_1,\, \cdots,\, a_l) - 整数:
c,\, c'
としたとき、
それでは、復号アルゴリズムを実装します。
# n^{-1}を計算する関数
def inverse(n, m):
for x in range(1, m):
if (n * x) % m == 1:
return x
# c'とw_iから平文を計算する関数
def knapsack_to_binary(d, w):
p = []
for weight in reversed(w):
if d >= weight:
p.append(1)
d -= weight
else:
p.append(0)
return list(reversed(p))
# バイナリを文字列に変換する関数
def binary_to_string(binary):
string = ""
for bits in binary:
char_code = sum([_ * (2 ** i) for i, _ in enumerate(reversed(bits))])
string += chr(char_code)
return string
# 復号する関数
def decrypt(w, m, c, n):
inverse_n = inverse(n, m)
flag = ""
for value in c:
d = (value * inverse_n) % m
binary = knapsack_to_binary(d, w)
flag += binary_to_string([binary])
if all(32 <= ord(e) <= 126 for e in flag):
return n, flag
return None, None
if __name__ == "__main__":
w = [1, 2, 4, 8, 16, 32, 64, 128] # knapsack list
m = 257
c = [538, 619, 944, 831, 360, 531, 468, 971, 635, 593, 655, 425, 1068, 530, 1068, 360, 706, 1068, 299, 619, 670, 1068, 891, 425, 670, 1068, 371, 670, 732, 531, 1068, 484, 372, 635, 371, 372, 237, 237, 1007] # encrypted string
for n in range(1, m):
n_value, flag = decrypt(w, m, c, n)
if n_value:
print(f"n: {n_value}, flag: {flag}")
$ py decode.py
n: 31, flag: ironCTF{M4th_&_C5_ar3_7h3_b3sT_c0Mb0!!}
$
正答:ironCTF{M4th_&_C5_ar3_7h3_b3sT_c0Mb0!!}
Discussion