[WARGAME][webhacking.kr] old-51

jckim22·2022년 11월 19일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
90/114

아래는 서버 코드이다.

<?php
  if($_POST['id'] && $_POST['pw']){
    $db = dbconnect();
    $input_id = addslashes($_POST['id']);
    $input_pw = md5($_POST['pw'],true);
    $result = mysqli_fetch_array(mysqli_query($db,"select id from chall51 where id='{$input_id}' and pw='{$input_pw}'"));
    if($result['id']) solve(51);
    if(!$result['id']) echo "<center><font color=green><h1>Wrong</h1></font></center>";
  }
?>

아래가 웹페이지인데 딱 봐도 sql injection의 냄새가 난다.

아래에 아이디와 비밀번호 부분에 인젝션을 해야한다.

id는 addslashes로 필터링 되어있고 이번에는 euc-kr utf-8로 인코딩 되어 있지도 않아서 멀티 바이트로 우회할 수 없다.
pw마저 md5로 암호화 되어 있어서 나는 무조건 id에서 우회하려고 했다.

아래와 같은 구문을 만들려고 별 노력을 다 해보았다.

select id from chall51 where id='admin' union select 3 --  and pw='비밀번호'

정말 오랜 시간동안 삽질을 했다.
addslashes를 우회할 수 있는 방법을 열심히 찾아보았지만 euc-kr utf-8로 인코딩 되어 있다는 가정하 밖에 없었다.

25점 짜리인데 정말 감을 잡지 못했다.
md5에 true라고 하는 것이 마음에 걸려서 찾아보았는데 16자리 바이너리 형식으로 md5를 해시해주는 속성이었다.
default는 false였고 이게 우리가 흔히 보던 32자리 문자열이였다.
그래도 감이 잡히지 않았고 설마 pw를 이용하는 걸까 했다.

그렇게 시간이 지난 후 결국 풀이를 보며 공부한 후 직접 풀게 되었다.

이 문제는 새로운 유형이어서 또 새로운 공부를 하게 되었고 많은 시도를 하는 시간들도 의미가 없는 시간은 아니였다고 생각한다.

풀이를 보고 공부한 것은 정말 기본적인 것이지만 논리 연산자를 이용한 방법이었다.

참 and 거짓 or 참 은 참이었고
참 and 거짓 = 거짓 도 참이었다 이유는 참 and (거짓=거짓)이기 때문이다.

이 두가지 중 하나를 이용해야 했다.

sql에서는 문자열의 첫 글자를 보고 참인지 아닌지 판별한다고 한다 1~9까지의 숫자가 첫 글자이면 참이고 0이나 다른 문자이면 거짓이다.

그렇다는 말은 or문이 나왔을 때는 or [1-9]~ 여야 성립할 수 있는 것이다.
물론 아이디에는 admin을 입력하여 참이 나오게 한다.

그리고 = 일때는 이 외에 문자들이 나와서 거짓이 되게 해야 성공할 수 있었다.

근데 보통 or는 아스키코드로 'o' 'r' 1-9의 숫자 총 5자리의 수가 연속되어야 문제를 풀 수 있기 때문에 어렵다고 한다.
그래서 아스키코드로 '=' 1-9이외의 모든 문자 를 찾아서 3자리만 맞으면 되는 =이 훨씬 확률이 높다.

그래서 = ![1-9]를 찾을 것이다.

아래 공부를 하고 나서 내가 직접 짠 코드를 보자.

import requests
from hashlib import md5


for i in range(9999999999):
    if(b'\x27\x3d\x27' in md5(str(i).encode()).digest()):
        print(f"{str(i)}={str(md5(str(i).encode()).digest())}")
        

코드를 보면 '=' 은 16진수로 27,3d,27 이기 때문에 \x27\x3d\x27을 찾아준다. 앞에 b는 byte를 뜻한다.

어디서 찾아주냐면 바로 md5(str(i).encode()).digest() 인데 해석하자면 i를 md5해준 해시 값에서 찾아주는 것이다.
하지만 파이썬에서 일반적으로 hexdigest를 하는 것이 아닌 digest를 했다.
hexdigest는 16진수 문자열을 쭉 나열해주지만 digest는 bytes단위로 끊어주게 된다.
md5(~~,true)에서 binary 형식으로 반환해준다 했으니 sql에서 바이트 단위로 끊어질 것이다.
그럼 digest를 통해 '='인 바이트 3개를 찾게 되면 문제를 풀 수 있는 것이다.

이번 문제를 통해 잘 안다고 생각했던 비트,바이트,16진수,10진수,utf-8등의 여러 기법과 단위들의 조합들을 더 공부할 수 있었다.

기본적으로 1바이트는 8비트인데 255범위의 숫자를 표현할 수 있고 곧 아스키 코드와 연동된다.
하지만 10진수로 표현하는 아스키 코드는 붙였을 때 구별이 어렵다.
4비트는 15범위의 숫자를 표현할 수 있다.
10,11,12 ~~ 를 알파벳으로 표현하기로 한 후 1바이트를 16진수 2개로 구분할 수 있게 되었다.
그래서 1바이트를 붙였을 때 구분하기 쉬워졌다.
16진수 2개가 1바이트 이기 때문이다.

sql에서는 바이너리 형식을 8비트 단위로 끊어서 1바이트씩 문자로 인식할 것이다.
나는 파이썬에서 digest로 바이트 단위로 구별해 16진수 2개가 합쳐져 1바이트인 문자을 구분할 것이고 그것은 \x3d인 =이다.
쿼터는 27이기 때문에 앞뒤로 2개를 붙여서 \x27\x36\x27인 조합이 md5 해시값에 있는지 확인한다.

아래는 그 결과이다.


위를 잘 보면 =뒤에 [1-9]가 아닌 것들이 많다.
그 중 하나를 골라서 아이디는 admin 비밀번호에는 그 숫자를 넣게 되면

select id from chall51 where id='admin' and pw='비밀번호'

가 되고 pw가 md5(pw,true)로 해시되면서 내가 구했던 바이트 단위로 sql에서 인식되고 '='이 생겨 참and (거짓=거짓)이 성사 되면 참이 되고 result가 true가 되면서 아래처럼 solve를 하게 된다.

profile
개발/보안

0개의 댓글