🔧
SadServers Writeups
#tech
#Linux
2023-11-1
SadServersというのがあり,Linuxサーバーのトラブルシューティングを行える.これを解いたのでその解法を記載する.解き終わってかつ,日本語を書く気持ちが出たものは順次ここに追記していく.
$ sudo rm /var/log/bad.log
$ cut -d- -f1 /home/admin/access.log | sort | uniq -c | sort
で一番下に最も多いreq数のやつが来るからこれを適当に書き込むと良い
2問構成.まず/home/admin
直下の.txtなファイルに対して,Aliceが登場する行の数を見る必要があるが,xargsを使うと便利.
$ ls *.txt | xargs grep -c Alice
これの出力を足し合わせて1問目クリア.次2問目.問題文曰く,Aliceの出現回数が1なファイルを見ると良くて,これが一意なので適当にlessで表示している最中に"/Alice"でAliceが存在する行まで移動して次の行を見ると答えが書いてあるのでクリア.
ポートノッキングされたサーバーがlocalhost:80
にあるから,これを開けるようにする.
$ curl localhost -v
* Trying 127.0.0.1:80...
* connect to 127.0.0.1 port 80 failed: Connection refused
* Failed to connect to localhost port 80: Connection refused
* Closing connection 0
curl: (7) Failed to connect to localhost port 80: Connection refused
まあそうだよねって気持ちになる.とりあえずポートスキャンしてみる:
$ nmap localhost
Starting Nmap 7.80 ( https://nmap.org ) at 2024-01-04 05:03 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00012s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
8080/tcp open http-proxy
Nmap done: 1 IP address (1 host up) scanned in 0.07 seconds
22と8080が空いている.knockd
に付属するknock
コマンドを用いると特定のポートをノックすることができる:
$ knock localhost 22
$ curl localhost
Who is there?
スペース区切りで1行に2つの数字が書かれたtxtがあるのでこの後者の方だけの平均を取る.
Tip: There's bc, Python3, Golang and sqlite3 installed in this VM.
とあるが,普通にシェルワンライナーができそう -> できた:
$ awk '{sum+=$2} END {printf "%.2f\n", int(sum*100/NR)/100}' ~/scores.txt > ~/solution
これで~/solution
のMD5が問題に書いてあるものと一致するのでクリア.
psqlに書き込みができないらしいが頑張りを発揮するとできるようになるらしい.ご丁寧にHelpful Postgres information: it's a service that listens to a port (:5432) and writes to disk in a data directory,
などと書いてあるので問題文を読んでいる際にディスクが一杯なのかな~という推測が立つ.実際にこれが原因なのかを確かめるには例えば次のようにする:
# sudo -u postgres psql -c "insert into persons(name) values ('jane smith');" -d dt
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
Is the server running locally and accepting connections on that socket?
# cat /var/log/postgresql/postgresql-14-main.log
2023-09-01 12:39:47.066 UTC [651] FATAL: could not create lock file "postmaster.pid": No space left on device
pg_ctl: could not start server
Examine the log output.
つまり適当にinsertしようとしてみてからログを見に行く.実際にはpsqlのデフォルトのログ置き場である/var/log/pgsql
ではなく/var/log/postgresql
にログが置かれていたので一度実行に失敗した.
まあけど要はディスクがないんだねってことでdfすると/opt/pgdataのUse%が100なので適当に中身を消してあげてからsystemctl restart postgresql
してinsertするとできるようになるのでクリア.
127.0.0.1:80
に立てたサイトがローカルから叩いても何の返答も無いらしい.案の定頑張りを発揮すれば解けるらしいのでFWが悪さをしている可能性を考慮してiptables -L
すると
Chain INPUT (policy ACCEPT)
target prot opt source destination
DROP tcp -- anywhere anywhere tcp dpt:http
となっていてはい.めんどいのでiptables -F
するとこの辺の設定を全て吹き飛ばせて便利.
じゃあこれでいけるかな~と思って勢いよくcurlを叩くもForbiddenのページが返ってくる.ローカルなものなのでhtmlファイルの権限を確認しに行くと600でなんやねんという気持ちになってパーミッションを適当に変えて
# curl localhost:80
すると所望のレスポンスが返る.これここまで書いて思ったけど何で問題名が全部都市名なん?
systemdで管理されたNginxにリクエストを送るもConnection refusedになるから直すと良いらしい.ので適当に
$ systemctl status nginx
してみるとunexpected ";" in /etc/nginx/sites-enabled/default:1
らしいのでそうなんだという気持ちで/etc/nginx/sites-enabled/default
から不要なセミコロンを消去してnginxを再起動してcurlするとInternal Server Error.ひょえー.
nginxのログを見たいので/var/log/nginx/error.log
を見るとToo many open filesとのこと.これは/etc/systemd/system/nginx.service
で設定できる(空にもできる)から見に行くと案の定LimitNOFILEが10になっているからこれを適当に998244353とかにするやんちゃをして再起動するとクリア.
Dockernizeされたアプリケーションがあるからこれを動かしてね!ただしFor the solution to be valid, there should be only one running Docker container.
だよ!らしい.まあDockerイメージ焼くだけで問題にならないのはそれはそう.
なのでまずは8888を取っているやつの正体を探る.これはcurl -v
とかするとnginxであることが分かるので適当にsystemd stop
しておく.これでConnection refusedになるのでこのポートが空いた.
次にDockerイメージを焼いて動かす.アプリケーションがあるディレクトリに移動して適当にdocker build & docker run
するとコケる.曰くError: Cannot find module '/usr/src/app/serve.js'
らしいのでDockerfile内で参照しているjsファイルが存在していない.似たような名前でserver.jsが存在するのでこれのtypoと判断し修正して動かす.まだ何かがおかしいので確認するとEXPOSE 8880
になっている.使うのは8888なので修正して動かすと動作するようになる.
シェルがコンテナ内で動いているのかVM内で動いているのかを判定してね!という問題.知らんかったのでググったけど例えば
$ ls -ali / | sed '2!d' |awk {'print $1'}
の結果が2だったらコンテナ外,そうでないならコンテナ内らしい.なるほどなあ.実際にはPodmanのコンテナ内部で想定解法は
cat /proc/1/environ|tr "\0" "\n"|grep container
らしい.まあDockerだと普通に/.dockerenv
の有無だけで分かってしまうしなあ.
/home/admin/somefile
があるプロセスによって書き込みモードで開かれているらしいが,このプロセスを殺すことなくファイルを閉じる問題.
まずはどのプロセスが開いているのかを観察する:
$ lsof /home/admin/somefile
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 817 admin 77w REG 259,1 0 272875 /home/admin/somefile
よってプロセスIDが817のbashがファイルディスクリプタ77番で開いているのを消せばよさそう.プロセスの保持するファイルディスクリプタは/proc
以下に存在するから確認してみる:
$ ls /proc/817/fd
0 1 2 255 77
これを消すにはexec
コマンドを用いるとできる:
$ exec 77<&-
これでlsof
しても何も表示されないのでクリア.
etcdという,KVSのサーバーがhttps://localhost:2379
で動いているけど問題があるから修正して正しくバリューを返すようにする.
実際にetcdctl get foo
をすると:
$ etcdctl get foo
Error: client: etcd cluster is unavailable or misconfigured; error #0: x509: certificate has expired or is not yet valid: current time 2024-12-03T08:27:59Z is after 2023-01-30T00:02:48Z
error #0: x509: certificate has expired or is not yet valid: current time 2024-12-03T08:27:59Z is after 2023-01-30T00:02:48Z
と言われ,SSL証明書が期限切れらしい.ということで現在のサーバー時間を確認してみると2024/12/3と言われた.これは確かに期限切れになるので:
$ sudo date -s "01/30 00:00 2023"
Mon Jan 30 00:00:00 UTC 2023
admin@ip-10-0-0-138:/$ date
Mon Jan 30 00:00:01 UTC 2023
admin@ip-10-0-0-138:/$ etcdctl get foo
Error: client: response is invalid json. The endpoint is probably not valid etcd cluster endpoint.
う~ん,何やら変わりおった.なんですかという気持ちでverboseしてほしい:
$ curl https://localhost:2379/v2/keys/foo -v
* Trying 127.0.0.1:2379...
* Connected to localhost (127.0.0.1) port 2379 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=localhost
* start date: Dec 31 00:02:48 2022 GMT
* expire date: Jan 30 00:02:48 2023 GMT
* common name: localhost (matched)
* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=localhost
* SSL certificate verify ok.
> GET /v2/keys/foo HTTP/1.1
> Host: localhost:2379
> User-Agent: curl/7.74.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Server: nginx/1.18.0
< Date: Mon, 30 Jan 2023 00:00:55 GMT
< Content-Type: text/html
< Content-Length: 153
< Connection: keep-alive
<
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>
* Connection #0 to host localhost left intact
けったいなので,Server: nginx/1.18.0
などと宣っている.普通にetcdが返せやという気持ちになるのでNATの設定を確認する:
$ sudo /usr/sbin/iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- anywhere anywhere ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
REDIRECT tcp -- anywhere anywhere tcp dpt:2379 redir ports 443
DOCKER all -- anywhere !ip-127-0-0-0.us-east-2.compute.internal/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- ip-172-17-0-0.us-east-2.compute.internal/16 anywhere
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- anywhere anywhere
見ると,ポート2379から443へのリダイレクトが走っているのでこれが原因っぽそう.設定をiptables -F
とかで吹き飛ばしてクリア.
~/kihei
を実行しようとすると死ぬから死なないようにするタスク.
まず普通に動かしてpanicすることを確認.無いかな~と思って-h
付けるとあって,ご丁寧に-v
が存在するらしい.ほえ~
admin@i-00ea3683be33c9605:~$ ./kihei -v
Creating file /home/admin/data/newdatafile with size 1.5GB...
panic: exit status 1
goroutine 1 [running]:
main.main()
./main.go:64 +0x47d
じゃあこの1.5GB作ろうとしているのがあかんねって気持ちになってdf
とかしてみると
$ df
Filesystem 1K-blocks Used Available Use% Mounted on
udev 221828 0 221828 0% /dev
tmpfs 46636 368 46268 1% /run
/dev/nvme0n1p1 8026128 6354488 1242384 84% /
tmpfs 233168 12 233156 1% /dev/shm
tmpfs 5120 0 5120 0% /run/lock
/dev/nvme0n1p15 126678 6016 120662 5% /boot/efi
なんや/dev/nvme0n1p1
って気持ちになってlsblk
する:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1 259:0 0 8G 0 disk
├─nvme0n1p1 259:2 0 7.9G 0 part /
├─nvme0n1p14 259:3 0 3M 0 part
└─nvme0n1p15 259:4 0 124M 0 part /boot/efi
nvme1n1 259:1 0 1G 0 disk
nvme2n1 259:5 0 1G 0 disk
/dev/nvme0n1p1
というやつが7.9GB食っている.これをいい感じにshirinkできれば空き領域が広がりそう:
$ sudo tune2fs -m1 /dev/nvme0n1p1
tune2fs 1.46.2 (28-Feb-2021)
Setting reserved blocks percentage to 1% (20643 blocks)
これで./kihei
するとpanicせずに実行できる.
スペースか改行か.,:;
のいずれかで区切られたテキスト群があるから2番目に多く出てくる単語を見つけるタスク.ただし大文字小文字は区別しないらしい.
Lhasaと同じで,PythonやGolangは入っているらしいが,シェルワンライナー縛りができそう -> できた:
$ tr -s '[:space:]' '\n' < frankestein.txt | tr '[:upper:]' '[:lower:]' | grep -E -o '\b[^,.;:"]+\b' | tr '[:lower:]' '[:upper:]' | sort | uniq -c | sort -nr | sed -n '2s/^[ \t]*[0-9]*[ \t]*//p' > ~/mysolution
WordPressのDockerコンテナがMariaDBのコンテナに接続できないと言っている.ので問題文にある通りのコマンドで接続してみようとする:
$ sudo docker exec wordpress mysqladmin -h mysql -u root -ppassword ping
mysqladmin: connect to server at 'mysql' failed
error: 'Unknown MySQL server host 'mysql' (-2)'
Check that mysqld is running on mysql and that the port is 3306.
You can check this by doing 'telnet mysql 3306'
DBのコンテナが動いているかを確認したいのでdocker ps
してみる:
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6ffb084b515c wordpress:sad "docker-entrypoint.s…" 16 months ago Up 22 seconds 0.0.0.0:80->80/tcp wordpress
0eef97284c44 mariadb:latest "docker-entrypoint.s…" 16 months ago Up 22 seconds 0.0.0.0:3306->3306/tcp mariadb
動いてはいそう.けどよしなにdocker-compose.yml
作ってdocker-compose up
すれば終わりな気配はあるのでとりあえず2つのコンテナを止める:
$ sudo docker stop mariadb wordpress
mariadb
wordpress
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
次にdocker-compose.yml
を作成する.このときのMYSQL_ROOT_PASSWORD
などの環境変数はコンテナが動いている際に適当にdocker inspect
することで確認した:
version: '3'
services:
mysql:
image: mariadb:latest
container_name: mysql
hostname: mysql
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: wordpress
wordpress:
depends_on:
- mysql
image: wordpress:sad
container_name: wordpress
hostname: wordpress
ports:
- 80:80
environment:
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: password
これでdocker-compose up
する.
Manhattanと同じように,psqlに対してinsertができないのを解決するタスク.
まずpsqlが立っているかどうかを確かめる:
# ps aux | grep postgre
root 703 0.0 0.1 4832 876 pts/0 S+ 04:52 0:00 grep postgre
立っていないのでsystemctl start postgresql
してから再度確認しても立っていない.このためログを確認する:
# journalctl -p err
-- Logs begin at Fri 2024-01-05 04:51:45 UTC, end at Fri 2024-01-05 04:54:11 UTC. --
Jan 05 04:51:45 ip-172-31-25-11 kernel: ena 0000:00:05.0: LLQ is not supported Fallback to host mode pol
Jan 05 04:51:50 ip-172-31-25-11 systemd[1]: Failed to start PostgreSQL Cluster 14-main.
Jan 05 04:53:15 ip-172-31-25-11 systemd[1]: Timed out waiting for device /dev/xvdb.
Jan 05 04:53:26 ip-172-31-25-11 systemd[1]: Failed to start PostgreSQL Cluster 14-main.
lines 1-5/5 (END)
/dev/xvdb
を待ってタイムアウトしているから,これはなんやということでlsblk
してみる:
# lsblk -f
/dev/xvdb
なんぞ無いから当然タイムアウトになる.ここで代わりに/dev/nvme0n1
などを使うことができそう.
systemdがどのデバイスを用いるかの設定は,
ローカルで動いているApacheのサーバー内に秘匿されている情報を取り出したい.この問題の特徴として,次のようなものが問題文に書かれている:
Also note that the password crackers Hashcat and Hydra are installed from packages and John the Ripper binaries have been built from source in /home/admin/john/run
ということはどこかでブルートフォースしそうだなというあたりをつけてVMを開く.
まずApacheのサーバーにリクエストを飛ばしてみる:
$ curl localhost:80 -v
* Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< Date: Fri, 08 Dec 2023 12:46:19 GMT
< Server: Apache/2.4.54 (Debian)
< WWW-Authenticate: Basic realm="Protected Content"
< Content-Length: 456
< Content-Type: text/html; charset=iso-8859-1
<
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Unauthorized</title>
</head><body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested. Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
<hr>
<address>Apache/2.4.54 (Debian) Server at localhost Port 80</address>
</body></html>
* Connection #0 to host localhost left intact
認証が通っていないらしい.Apacheの認証設定は/etc/apache2/sites-enabled/000-default.conf
にあり,BASIC認証をしている:
$ cat /etc/apache2/sites-enabled/000-default.conf
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory "/var/www/html">
AuthType Basic
AuthName "Protected Content"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
</Directory>
</VirtualHost>
このパスワードをハッシュ化したものは/etc/apache2/.htpasswd
にあるといっている:
$ cat /etc/apache2/.htpasswd
carlos:$apr1$b1kyfnHB$yRHwzbuKSMyW62QTnGYCb0
このパスワードを破るとよさそう.これにはJohn the Ripperが使える:
$ ./john /etc/apache2/.htpasswd
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3])
Will run 2 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
Warning: Only 34 candidates buffered for the current salt, minimum 48 needed for performance.
Almost done: Processing the remaining buffered candidate passwords, if any.
0g 0:00:00:00 DONE 1/3 (2023-12-08 12:49) 0g/s 3613p/s 3613c/s 3613C/s Carlos1921..Carlos1900
Proceeding with wordlist:./password.lst
Enabling duplicate candidate password suppressor
chalet (carlos)
1g 0:00:00:02 DONE 2/3 (2023-12-08 12:49) 0.4716g/s 31377p/s 31377c/s 31377C/s 050381..song
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
carlosというユーザーのPWはchaletであることが分かる.これを使ってlocalhost:80
にログインする:
$ curl localhost:80 -u "carlos:chalet"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /</title>
</head>
<body>
<h1>Index of /</h1>
<table>
<tr><th valign="top"><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr>
<tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="webfile">webfile</a></td><td align="right">2023-02-13 02:39 </td><td align="right">215 </td><td> </td></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
<address>Apache/2.4.54 (Debian) Server at localhost Port 80</address>
</body></html>
localhost:80/webfile
に何かがありそうなので見てみると,何かのバイナリファイルが落ちてくるらしいから--ouput
で受け取る:
$ curl localhost:80/webfile -u "carlos:chalet" --output res
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 215 100 215 0 0 209k 0 --:--:-- --:--:-- --:--:-- 209k
これはzipファイルらしいのでunzip
しようとするとPWがかかっている.なのでこれもJohn the Ripperにかけるとよさそう:
$ ./zip2john ~/res
ver 1.0 efh 5455 efh 7875 res/secret.txt PKZIP Encr: 2b chk, TS_chk, cmplen=29, decmplen=17, crc=AAC6E9AF ts=14E0 cs=14e0 type=0
res/secret.txt:$pkzip$1*2*2*0*1d*11*aac6e9af*0*44*0*1d*14e0*7307b809c29d0e4602770428f8f469ba44ff98065855e557f76e29e8d1*$/pkzip$:secret.txt:res::/home/admin/res
admin@i-0af09adc22e1edf02:~/john/run$ ./zip2john ~/res > ~/hash
ver 1.0 efh 5455 efh 7875 res/secret.txt PKZIP Encr: 2b chk, TS_chk, cmplen=29, decmplen=17, crc=AAC6E9AF ts=14E0 cs=14e0 type=0
admin@i-0af09adc22e1edf02:~/john/run$ ./john ~/hash
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
0g 0:00:00:00 DONE 1/3 (2023-12-08 12:54) 0g/s 175033p/s 175033c/s 175033C/s Txtres1900..Tsecret1900
Proceeding with wordlist:./password.lst
Enabling duplicate candidate password suppressor
andes (res/secret.txt)
1g 0:00:00:01 DONE 2/3 (2023-12-08 12:54) 0.8333g/s 469025p/s 469025c/s 469025C/s poussinet..nisa1234
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
zipのPWが分かったから開けてクリア.
localhost:5000
上のサーバーに存在するフォームに正しい文字列をポストしてflagをゲットするという内容.
Descriptionからだけだと何も分からないのでとりあえずcurl
してみる:
$ curl localhost:5000 -v
* Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET / HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: Werkzeug/3.0.0 Python/3.9.2
< Date: Thu, 04 Jan 2024 04:16:57 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 217
< Connection: close
<
<form method="POST">
<label for="password">Password:</label>
<input type="password" id="password" name="password">
<input type="submit" value="Submit">
</form>
* Closing connection 0
Pythonで動いているのは分かるが,それ以上の情報が無いやねという気持ちになる.
分からんねの気持ちで何となくls -a
すると
$ ls -a
. .. .ansible .bash_history .bash_logout .bashrc .config .git .profile .ssh agent
.git
があるらしい.そうなんだということでgit status
するとPythonファイルが1つ消されているらしい.中身を見てみる:
$ git diff
diff --git a/webserver_v1.py b/webserver_v1.py
deleted file mode 100755
index e1ac37d..0000000
--- a/webserver_v1.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from flask import Flask, request
-import os
-
-app = Flask(__name__)
-
-
[email protected]('/', methods=['GET', 'POST'])
-def home():
- if request.method == 'POST':
- entered_password = request.form.get('password')
- super_secret_password = os.environ.get('SUPERSECRETPASSWORD')
-
- if entered_password == super_secret_password:
- return 'Access granted!'
- # DO STUFF HERE
-
- return 'Access denied!'
-
- return '''
- <form method="POST">
- <label for="password">Password:</label>
- <input type="password" id="password" name="password">
- <input type="submit" value="Submit">
- </form>
- '''
-
-
-if __name__ == '__main__':
- app.run()
ほーん,$SUPERSECRETPASSWORD
との一致を見ているんやねという気持ちになるので勢いよく中身を見ようとしてみる:
$ echo $SUPERSECRETPASSWORD
そうなんだ.けど無いわけなくない?という気持ちになるからこれ以外の方法であるんだろうなの気持ちになる.
ところでシェルから起動したプロセスは,シェルと同一の環境変数を用いているとは限らない.実際にどのような環境変数を用いているかは/proc/${PID}/environ
を見ると良い.そのためにまずはlocalhost:5000
上で動いているプロセスのPIDを知りたい:
$ lsof -i :5000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 572 admin 3u IPv4 11732 0t0 TCP localhost:5000 (LISTEN)
PIDは572だから次のようにして環境変数の一覧を得る:
$ cat /proc/572/environ
LANG=C.UTF-8PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOME=/home/adminLOGNAME=adminUSER=adminSHELL=/bin/bashINVOCATION_ID=af634a88cbb34fb596a836e07c4ede4aJOURNAL_STREAM=8:11516SUPERSECRETPASSWORD=bdFBkE4suaCy
$SUPERSECRETPASSWORD
が分かったのでflagを入手しに行く:
$ curl -d "password=bdFBkE4suaCy" -XPOST localhost:5000
Access granted! Secret is QhyjuI98BBvf