n费马分解再rsa
flag:
import libnum
import gmpy2
from Crypto.Util.number import *
p = 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483
q = 11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407
e = 65537
c = 48757373363225981717076130816529380470563968650367175499612268073517990636849798038662283440350470812898424299904371831068541394247432423751879457624606194334196130444478878533092854342610288522236409554286954091860638388043037601371807379269588474814290382239910358697485110591812060488786552463208464541069
n = 52147017298260357180329101776864095134806848020663558064141648200366079331962132411967917697877875277103045755972006084078559453777291403087575061382674872573336431876500128247133861957730154418461680506403680189755399752882558438393107151815794295272358955300914752523377417192504702798450787430403387076153
p = gmpy2.iroot(n,2)[0]-10000
while not isPrime(p) : p += 1
q = p + 2
while not isPrime(q) : q += 2
while p*q - n != 0 :
p = q
q += 2
while not isPrime(q) : q += 2
phi_n = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi_n)
m = pow(c, d, n)
print(m)
flag = libnum.n2s(int(m))
print(flag)
b'flag{9cd4b35a-affc-422a-9862-58e1cc3ff8d2}'
4C4A575851324332474E324547554B494A5A4446513653434E564D444154545A4B354D45454D434E4959345536544B474D5134513D3D3D3D
base16
base32
base64
flag{B@sE_0f_CrYpt0_N0W}
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
flag
{flag{0ops!_you_know_XOR!}}
LSB隐写
SilentEye打开发现里面有二维码,直接扫
F12
藏在源代码里,还给了下一关的目录
<!-- flag第一部分:ZmxhZ3tXQTB3,开始你的新学期吧!:/4cqu1siti0n -->
稍微看一下代码,await等待接受一个参数className,这个参数跟在/api/flag/,就是我们第一关源码泄露出来的目录名,这段代码接收后就将POST请求头补充完整,否则post请求就不会传递任何信息,导致后一个if里的response.ok的bool值为0,所以只需要我们在控制台里将className的正确值给他
只需要调用revealFlag函数,将目录名传给他,一开始传的格式不太对,后来发现不需要加/因为他的代码里已经有/了,所以只需要给名字就ok
恭喜你!你获得了第二部分的 flag: IV95NF9yM2Fs
……
时光荏苒,你成长了很多,也发生了一些事情。去看看吧:/s34l
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('seal_him');
const stateElement = document.getElementById('state');
const messageElement = document.getElementById('message');
form.addEventListener('submit', async function (event) {
event.preventDefault();
if (stateElement.textContent.trim() !== '解封') {
messageElement.textContent = '如何是好?';
return;
}
try {
const response = await fetch('/api/flag/s34l', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ csrf_token: document.getElementById('csrf_token').value })
});
if (response.ok) {
const data = await response.json();
messageElement.textContent = `第三部分Flag: ${data.flag}, 你解救了五条悟!下一关: /${data.nextLevel || '无'}`;
} else {
messageElement.textContent = '请求失败,请重试。';
}
} catch (error) {
messageElement.textContent = '请求过程中出现错误,请重试。';
}
});
});
在控制台输入
document.getElementById('state').textContent = '解封';
然后点解封
第三部分Flag: MXlfR3I0c1B, 你解救了五条悟!下一关: /Ap3x
禁用js
{
"flag": "fSkpKcyF9",
"nextLevel": null
}
ZmxhZ3tXQTB3IV95NF9yM2FsMXlfR3I0c1BfSkpKcyF9
一眼base64
flag{WA0w!_y4_r3al1y_Gr4sP_JJJs!}
进robots.txt会给一个目录,进去看到是一段PHP脚本
题目分析
<?php
function execute_cmd($cmd) {
system($cmd); //ls
}
function decrypt_request($cmd, $key) {
$decoded_key = base64_decode($key); //key值需要base64编码 并赋值给了$decoded_key N2FiZThiMjRiZDAxMzc0NDZmZDMzNmMyMjk4ZmNjYTA=
$reversed_cmd = '';
for ($i = strlen($cmd) - 1; $i >= 0; $i--) { //i为cmd的len-1 =MHb v4iLgMHb
$reversed_cmd .= $cmd[$i]; //将cmd进行了reverse,并赋值给reversed_cmd
}
$hashed_reversed_cmd = md5($reversed_cmd); //将reversed_cmd进行了md5处理 7abe8b24bd0137446fd336c2298fcca0
if ($hashed_reversed_cmd !== $decoded_key) { //需要令hashed_reversed_cmd 与 decoded_key 相等
die("Invalid key");
}
$decrypted_cmd = base64_decode($cmd); // cmd : bHM= bHMgLi4v ==gCv4iLv4iLgMHb
return $decrypted_cmd;
}
if (isset($_POST['cmd']) && isset($_POST['key'])) {
execute_cmd(decrypt_request($_POST['cmd'],$_POST['key']));
}
else {
highlight_file(__FILE__);
}
//将cmd需要的指令base64,reverse,md5,base64就是key,指令的base64就是cmd
EXP
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
0
最终payload:
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
1
抓包直接在response里
多次尝试后发现没有闭合符,是整形注入
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
2
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
3
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
4
3报错,得到字段数为2
这里尝试爆库名时,一直回显为
然后发现让他前面查询结果为-1,查询失败就可以回显了
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
5
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
6
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
7
打开御剑发现了一个目录,访问后给了一个zip压缩包
里面有一个网页的HTML,两段PHP代码:index.php pizwww.php
index.php:
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
8
访问index.php自动重定向到index.html
pizwww.php:
from Crypto.Util.number import bytes_to_long, long_to_bytes
# 已知的密钥和加密后的数据
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
# 恢复 m1
m1 = c1 ^ bytes_to_long(key)
m1_bytes = long_to_bytes(m1)
# 恢复 m2
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
m2 = xor_bytes(c2, key)
# 拼接 m1 和 m2 得到完整的 flag
flag = 'flag{' + m1_bytes.decode('utf-8') + m2.decode('utf-8') + '}'
print(flag)
9
hash强比较,数组绕过,过滤cat和flag,用tac和*通配符
exp:
http://eci-2ze8d3ln9lmig6vvuqa5.cloudeci1.ichunqiu.com/pizwww.php?new[]=1
POST:star[]=2&cmd=system('tac /fla*');
进题目后发现需要让我们在一秒内输入八句英文才算成功,个人尝试过对js控制台进行操作,使其时间固定为1s之内的数值,并且js代码禁止了粘贴,直接禁用js又会导致根本使用不了这个按钮,尝试过抓包直接发送数据,但是发现回显是:需要自动重定向到target url,而且直接观察也能发现每一次的英文句子是会发生改变的,去抓包发送数据会发错误的数据。于是我想到了使用selenium库,用web自动化测试脚本去操作,这下面是题目对我们限制的js代码(已注释)
{flag{0ops!_you_know_XOR!}}
0
附上exp
{flag{0ops!_you_know_XOR!}}
1
payload
{flag{0ops!_you_know_XOR!}}
2
一个简单的脱壳题,拿到文件直接脱壳
{flag{0ops!_you_know_XOR!}}
3
脱完壳之后ida反编译
进入RC4函数,发现是一个RC4加密在与data比对,而RC4是流密码,只需要将data拿来RC4加密一次就可以得到flag,这里直接动调,利用LAZYida将输入的替换为data,即可得到flag
个人感觉这题不是简单,首先了解tea加密,密钥长度是64位,先用ida查看反编译,之后改一下函数名跟变量名使其更好分析
可以看到是进行一次tea加密后,将加密后的数据与unk_140004080做对比,进入tea加密函数
第一个要注意的就是 v4 -= 0x61C88647;正常来说这个位置是加,但是因为他是int类型,所有使用其补码,为9E3779B9
而对于密钥,因为其是DWORD类型,需要将字符串转换为数组大小为4的密钥,这里需要手动将其替换成key
为小端序
{flag{0ops!_you_know_XOR!}}
4
最后写出解密脚本
{flag{0ops!_you_know_XOR!}}
5
运行得到flag
同时用ida打开father与son,先查看ida的反编译
不理解ptrace(PTRACE_POKEDATA, addr, addr, 3);的意思,直接去问ai:
具体来说,ptrace(PTRACE_POKEDATA, pid, addr, value)
的参数含义如下:
PTRACE_POKEDATA
:指定要执行的操作类型,这里是向被调试进程的数据空间写入数据。pid
:被调试进程的进程ID。addr
:在被调试进程地址空间中的地址,你希望在这个地址处写入数据。value
:要写入的数据值。在你的例子中:
addr
被用作 pid
和 addr
参数,这意味着你要向被调试进程(其PID为addr
)的数据空间中某个地址(同样为addr
)写入数值 3
。需要注意的是,这里的两个 addr
可能是误用或者是特定场景下的需求,通常情况下,这两个参数应该是不同的,一个是目标进程的PID,另一个是你想写入数据的具体地址。
此外,使用 ptrace
需要有适当的权限,通常只有当调用进程具有对目标进程足够的权限时才能成功执行此类操作。这通常意味着调用者必须是超级用户或者两个进程之间存在特定的关系(如父子关系)。如果权限不足,ptrace
调用将会失败,并返回错误码。
也就是说,传入了3到addr这个地址,去看看addr指向哪里
跳转至地址处,发现在father没有这个地方,其实是在son文件里,son中跳转至该处
此时dword_60004040就是传入该处的3,而byte_60004020就是与输入加密后做对比的数据,所有可以得到解密脚本
{flag{0ops!_you_know_XOR!}}
6
打开jadx反编译它,找到加密函数
发现先进行了AES,base64加密,后面又进入了连接库,用ida分析.so文件
可以看到将经过上面加密的s再进行了enc加密处理,再与mm比对来判断用户输入的flag是否正确,分析enc函数
再分析encc函数
可以发现这其实是一个rc4,所有可以直接写出脚本求出进入so层加密前的数据
{flag{0ops!_you_know_XOR!}}
7
再将这段拿去AES解密即可得到flag
顺嘴一提这里的密钥是app的标题
直接按照提示nop掉所有的汇编后,分析发现只进行了简单的异或,这里直接给出脚本
{flag{0ops!_you_know_XOR!}}
8