Hack The Box——ForwardSlash

目录

简介

信息收集

漏洞发现

文件包含漏洞

漏洞利用

用户chiv的Shell

权限提升

解密ciphertext

用户pain的Shell

用户root的Shell

总结


简介

这是一台练习CTF的好靶机,靶机创作者有很多提示,但是也有很多坑,总体来说有一定的难度,涉及文件包含、加解密、逆向分析等知识。通过提示发现备份站点是关键,然后利用文件包含漏洞收集信息发现FTP登录凭据,使用SSH获得用户chiv的Shell,通过pain用户的SUID执行文件读取备份文件获得用户pain的MySQL数据库凭据,同样可以使用SSH获得Shell,通过枚举攻击解密密文,获得加密磁盘镜像的密码,从而获得root用户的SSH私钥文件,得到root权限Shell。

信息收集

使用nmap扫描扫描目标主机开放的常用端口及运行的服务,发现开启22和80端口,分别运行着ssh和http服务,且目标主机操作系统可能为ubuntu 2.6.32,如图:

然后访问80端口的http服务,发现网站被重定向到http://forwardslash.htb/,如图:

然后将forwardslash.htb添加到hosts文件再进行访问,如图:

网站名称是反斜杠派,而主机名称是正斜杠,从网页内容中发现来自反斜杠派的嘲讽,服务器使用了XML和FTP自动登录,反斜杠派可能利用这点黑了该网站,且他们是Sharon的忠实追随者。使用Google搜索关键字,只搜索到Apache反斜杠目录遍历漏洞,但对该靶机不适用。使用dirbuster扫描网站目录和php,bak,zip,sql等文件未发现有价值的信息,但在模糊测试时发现存在.php文件(也可能是目录),但是没有权限访问,如图:

使用wfuzz扫描目录的时候发现存在.htaccess和.htpasswd文件,以及server-status目录,但都没有访问权限,如图:

再次扫描网站txt文件发现存在note.txt文件,如图:

查看内容发现反斜杠派有备份站点,如图:

然后使用wfuzz利用Host头枚举子域名,当Host为backup.forwardslash.htb时发生跳转,如图:

将backup.forwardslash.htb添加到hosts文件,然后访问,如图:

页面跳转到登录页面。扫描网站目录及文件发现如下文件,如图:

漏洞发现

测试登录框发现不存在SQL注入漏洞,然后注册一个账户,如图:

登录之后跳转到welcome页面,如图:

文件包含漏洞

跳过重置密码,登出和修改用户名功能,查看修改个人资料图片,如图:

但是表单被禁用了,然后定位到submit按钮源码,发现有disabled属性,URL表单同样如此,如图:

将该属性删掉后就可以使用了,如图:

使用BurpSuite拦截数据包,将test.com改为http://127.0.0.1,如图:

页面返回index.php文件内容,如图:

说明此处存在远程文件包含漏洞。后来发现直接使用绝对路径也可以读取文件内容,所以此处也存在本地文件包含漏洞。

漏洞利用

利用该漏洞读取/etc/passwd文件,发现存在普通用户pain和chiv,如图:

然后在Kali Linux创建一个包含php代码的txt文件,并利用该漏洞包含此文件,如图:

发现并没有执行该代码,而仅仅是将文件内容显示了出来。然后读取/var/www/html/index.php文件内容,如图:

该文件是forwardslash.htb网站的index.php文件。尝试使用相对路径读取backup.forwardslash.htb网站的index.php文件,如图:

没有权限查看,尝试使用,base64编码读取,如图:

解码之后,如图:

这说明可以执行同级目录下的php文件,但没有权限在同级目录下创建php文件,因此无法利用该漏洞直接getshell。然后读取其他文件,在config.php中包含数据库用户名和密码等信息,如下:

<?php
//credentials for the temp db while we recover, had to backup old config, didn't want it getting compromised -pain
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'www-data');
define('DB_PASSWORD', '5iIwJX0C2nZiIhkLYE7n314VcKNx8uMkxfLvCTz2USGY180ocz3FQuVtdCy3dAgIMK3Y8XFZv9fBi6OwG6OYxoAVnhaQkm7r2ec');
define('DB_NAME', 'site');
 
/* Attempt to connect to MySQL database */
$link = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
 
// Check connection
if($link === false){
    die("ERROR: Could not connect. " . mysqli_connect_error());
}
?>

用户chiv的Shell

在dev/index.php文件中发现ftp用户chiv的密码,如图:

尝试使用22端口的ssh服务登录成功,如图:

权限提升

查看内核版本,如图:

内核版本符合CVE-2019-13272,但无法利用成功,使用CVE-2018-18955也无法利用成功。

解密ciphertext

查看发现user.txt文件在/home/pain目录下,且note.txt文件提示重要文件被加密了,如图:

查看encryptorinator目录下的文件发现密文和python加密程序文件,如图:

python加密程序内容如下:

def encrypt(key, msg):
    key = list(key)
    msg = list(msg)
    for char_key in key:
        for i in range(len(msg)):
            if i == 0:
                tmp = ord(msg[i]) + ord(char_key) + ord(msg[-1])
            else:
                tmp = ord(msg[i]) + ord(char_key) + ord(msg[i-1])

            while tmp > 255:
                tmp -= 256
            msg[i] = chr(tmp)
    return ''.join(msg)

def decrypt(key, msg):
    key = list(key)
    msg = list(msg)
    for char_key in reversed(key):
        for i in reversed(range(len(msg))):
            if i == 0:
                tmp = ord(msg[i]) - (ord(char_key) + ord(msg[-1]))
            else:
                tmp = ord(msg[i]) - (ord(char_key) + ord(msg[i-1]))
            while tmp < 0:
                tmp += 256
            msg[i] = chr(tmp)
    return ''.join(msg)


print encrypt('REDACTED', 'REDACTED')
print decrypt('REDACTED', encrypt('REDACTED', 'REDACTED'))

代码中包含一个加密函数、一个解密函数和一组测试数据,加密算法是对称密码,加密密钥和解密密钥是相同的,但是密钥会在哪里呢。

利用明文是可打印的字符串这一特性,编写python程序进行枚举,但是没有获得明文,这说明要么rockyou.txt中没有密码,要么明文中包含有不可打印的字符,读取密文发现密文长度为165,说明明文也是165个字符,假设可打印字符为160个,然后逐渐减少可打印字符数量,直到有满足条件的明文出现,程序decrypter.py如下:

#!/usr/bin/python
from threading import Thread
import linecache
import string

def decrypt(key, msg):
    key = list(key)
    msg = list(msg)
    for char_key in reversed(key):
        for i in reversed(range(len(msg))):
            if i == 0:
                tmp = ord(msg[i]) - (ord(char_key) + ord(msg[-1]))
            else:
                tmp = ord(msg[i]) - (ord(char_key) + ord(msg[i-1]))
            while tmp < 0:
                tmp += 256
            msg[i] = chr(tmp)
    return ''.join(msg)

def brute(filename,start,end,ciphertext):
    global isExit
    while start < end:
        #If one thread decrypts successfully, all threads are terminated
        if isExit:
            return 0
        #Read the file from start line to end line
        password=linecache.getline(filename,start)
        plaintext=decrypt(password,ciphertext)
        #If the number of printable characters is greater than 160, the decrypted plaintext is output
        if sum(i in string.printable for i in plaintext)>160:
            print "\033[1;32;1m[+] Password is :",password,"\033[0m"
            print "[+] Plaintext is :",plaintext
            isExit=True
        start+=1

def threadbrute(func,filename,lines,thread):
    threadlist=[]
    ciphertext=open('ciphertext','r').read()
    for i in range(thread-1):
        threadlist.append(Thread(target=func,args=(filename,i*lines,lines*(i+1),ciphertext)))

        threadlist.append(Thread(target=func,args=(filename,(thread-1)*lines,crows,ciphertext)))

    for i in threadlist:
        #Set thread as guardian thread,Allow termination via CTRL + C
        i.setDaemon(True)
        i.start()
        try:
            while i.isAlive():
                pass
        except KeyboardInterrupt:
            print "[!] Stop by KeyboardInterrupt!"
#Exit flag
isExit=False
wordlist='/usr/share/wordlists/rockyou.txt'

#Get dictionary lines
crows=len(linecache.getlines(wordlist))
#Divide the dictionary into blocks equal to the number of threads
lines,ad=divmod(crows,200)

print("[*] Cracking Password ......")
threadbrute(brute,wordlist,lines,200)

可打印字符大于160时未获得满足条件的输出,当可打印字符大于155时解密成功,明文中告知了/var/backups/recovery目录下加密映像的密钥,如图:

但是当前用户没有权限查看。

用户pain的Shell

然后继续查看其他文件,在/var/backups目录下发现另一个提示和config.php.bak文件,如图:

文件只对所有者pain用户具有读写权限,且提示需要密码才能读取。使用提权辅助脚本LinEnum.sh枚举发现所有者为pain的SUID文件,如图:

使用linux-smart-enumeration枚举,同样发现SUID文件,如图:

尝试运行发现提示并报错,提示还没有读到正确的文件,只有在同一秒钟内执行备份时才有效。且报错内容为32位16进制字符串不存在或不可访问,如图:

验证发现32位16进制字符串是当前时间戳的MD5值,如图:

从报错信息结合/var/backups/note.txt文件提示推测,当前时间戳的MD5值就是Pain所说的密码,backup程序应该是读取文件名为当前时间戳MD5值的文件内容,但是需要在一秒内将当前时间戳MD5的值作为文件名指向/var/backups/config.php.bak,并执行/usr/bin/backup程序,这就需要写个程序来实现了。使用bash命令更加简单:

ln -s /var/backups/config.php.bak $(date | cut -d' ' -f4 | tr -d $'\n' | md5sum | cut -d' ' -f1); backup

使用该命令成功读取到config.php.bak文件内容,但这仅仅是MySQL数据库用户pain的凭据,如图:

很奇怪使用该凭据并不能成功登录MySQL,但是却可以使用ssh进行登录,如图:

但这需要足够细心和很强的逻辑推理能力,我也是看了大佬的提示花了很长时间才明白backup程序的功能。也有大佬使用ltrace和strace调试分析程序的功能,当然我更喜欢使用IDA,如图:

可以清晰的看到,当MD5值的文件不存在是输出错误,否则就读取文件内容。

用户root的Shell

再次使用提权辅助脚本LinEnum.sh发现无需密码就可使用sudo的命令,但命令有限制,如图:

回到之前解密后的明文中提到的/var/backups/recovery目录,发现加密的备份镜像文件,如图:

使用提权辅助脚本枚举到的无需密码的sudo命令结合前边获取到的加密映像的密钥,解密并映射到/dev/mapper/backup,如图:

然后在具有写权限的目录下创建mnt目录,使用sudo /bin/mount /dev/mapper/backup ./mnt/进行挂载,如图:

查看目录内容发现id_rsa文件,如图:

应该是root用户的ssh私钥,使用该文件成功登录,如图:

总结

在该靶机刚发布不久我没能成功攻破,由于忽略了对txt文件的扫描,没能找到作者提示的备份站点。在该靶机退休后看了下大佬们的思路,然后又学习了一遍。作者给的提示也很多,但是有时候提示太多也容易混乱,而且坑也比较多。首先在文件包含漏洞那里花了很长时间,以为直接是通过文件包含漏洞获得Shell,在发现密文后,解密过程又花了很多时间,研究backup程序的功能也是,猜了很久,最后还是用了IDA分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值