#lab

lab7 server

developer

lab7 服务器使用

  • 选填题
  • docker
  • task0 签到
  • task1 符号链接
  • task2 资源
  • task3 ps
  • task4 恶意进程
  • task5 并行
  • task6 日志
  • task7 git

选填题

选择

  1. sudo

    添加用户、进入root需要sudo,挂载文件系统需要使用mount命令访问/dev/sdX,也需要sudo。

  2. 可疑用户

    攻击者往往用僵尸网络来爆破,账号密码简单。

  3. 编辑器

    vim和nano是常见编辑器。awk也可以生成新文件,但是cat只能查看不能编辑。

  4. 后台运行

    tmux、nohup和screen都可以,&则依赖于当前的shell,断开ssh就没了。

  5. 抓包工具

    Nessus是漏洞扫描工具。Wireshark是GUI,tcpdump是CLI,Burp Suite专精Web。

填空

  1. pypi

    管理python包的最方便方式还是虚拟环境,用conda环境随便装。另外还可以备份环境避免玩坏了。

  2. 查看端口

    相比于netstat,ss是自带的。lsof本是list open files,但端口也是一种文件,参数 -i 是Internet。

docker

原理

docker是一个轻量级的容器,运行在一个共享的Linux内核上,通过namespace和cgroup实现隔离。

安装

curl -fsSL https://get.docker.com -o get-docker.sh

sudo sh ./get-docker.sh

sudo systemctl start docker
sudo systemctl status docker

rootless

sudo apt-get install -y uidmap

/usr/bin/dockerd-rootless-setuptool.sh install

命令

# 加载容器
docker load -i server_lab.tar
# 运行容器
docker run --name server_lab -it -d server_lab 
# 列出所有容器
docker ps
# 执行容器内命令
docker exec –it server_lab bash

task0 签到

  • 任务要求:使用ssh连接到远程服务器,并运行指定目录下面的二进制文件,即可获取flag
  1. 进入task0_login目录

    1. cd /opt/tasks/task0_login
  2. 运行check文件

    1. ./check
  3. 获取ID和flag

    1. 该题目为签到题,没有设置检查点,运行后可直接获取flag

使用的服务器是我的Web服务器,通过内网访问。

截屏2025-07-30 15.19.10

截屏2025-07-26 16.58.25

task1 符号链接

  • 任务要求:通过执行命令使得文件夹/home/student/data/mnt/examdata 保持相同的读写行为
  • 完成要求之后,运行task1_file目录下面的check文件获取flag
  • 注意事项:在你执行正确的命令之前,确保/home/student/data目录是不存在的,否则check可能会失效

题解

这实际上就是创建符号链接。

ln -s /mnt/examdata /home/student/data

截屏2025-07-26 17.02.09

task2 资源查看

  • 任务目的:了解常用的查看资源的命令与方法
  • 任务要求:分别执行至少两种不同的命令用于查看内存、磁盘、CPU使用情况。
  • 注意事项:运行check之前,需要提前运行history -w命令确保你执行的命令被记录

内存

# 快速查看内存情况
free -h
# 更详细
vmstat -s

磁盘

# 查看挂载的整个文件系统
df -h
# 查看某个目录
du -sh /home

CPU

# 动态显示各种用量
top
htop
# 查看每个核的用量
mpstat -P ALL 1 1

截屏2025-07-26 17.18.10

task3 进程管理

  • 任务目的:掌握进程查询与释放的基本方法,了解僵尸进程
  • 任务要求:查找进程列表中的僵尸进程,并通过杀死其父进程的方式kill僵尸进程
  • 注意事项:运行check之前,确保你已经查询并杀死了僵尸进程,且使用history -w记录了你执行的命令

ps

ps常用参数

  • ps -e: everyone
  • ps -ef: everyone, fullformat
  • ps aux: BSD风格
  • ps -o xxx: option
    • pid
    • ppid
    • user
    • stat: Running, Sleeping, Zombie, sTopped
    • cmd
  • ps -u <user> -p <pid> -C <cmd>

ps输出格式

  • ef System V
    • UID PID PPID C STIME TTY TIME CMD
  • aux BSD
    • USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND

寻找Zombie

ps -ef | grep defunct

这种方法使用关键字defunct,这是因为僵尸进程的CMD字段会显示为[command_name] <defunct>

ps -eo pid,ppid,stat,cmd | awk '$3=="Z+"'

这种方法使用STAT字段。综上得到僵尸进程为

student       13      11  0 Jul26 pts/0    00:00:00 [python] <defunct>

kill

kill -9 11

截屏2025-07-29 13.22.48

task4 恶意进程

  • 任务目的:了解恶意进程的伪装方法以及基本的溯源
  • 任务要求:寻找恶意进程伪装使用的COMMAND,以及真实的COMMAND,并杀死恶意进程
  • 注意事项:
    • 此题目有一定的难度,真实的COMMAND需要包括启动的命令以及具体的文件目录
    • 真实COMMAND 不包含 &,命令是最简形式

ps

ps -ef输出

student        1       0  0 Jul26 pts/0    00:02:26 /bin/bash /tmp/.entrypoint.sh tail -f /dev/null
root           9       1  0 Jul26 ?        00:00:00 sshd: /usr/sbin/sshd [listener] 0 of 10-100 startups
student       10       1  0 Jul26 pts/0    00:00:08 [kworker/u8:2-events]
student      337       0  0 Jul26 pts/1    00:00:00 bash
student   293861       0  0 05:01 pts/2    00:00:00 bash
student   295470       1  0 05:23 pts/0    00:00:00 sleep 1
student   295471  293861  0 05:23 pts/2    00:00:00 ps -ef

可以看到pid==1的进程执行了一个可疑脚本/tmp/.entrypoint.sh,并且fork出了sshd、kworker、sleep等等。命令尾部的tail -f /dev/null的作用是挂起当前进程不退出的一种手法。

/tmp/.entrypoint.sh

#!/bin/bash

sudo /usr/sbin/sshd

python /tmp/.ftpd.py &
pid_ftpd=$!

python /tmp/sshd.py &
pid_ssh=$!

trap 'kill $pid_ftpd $pid_ssh 2>/dev/null; exit 0' SIGINT SIGTERM EXIT

while true; do
  for pid in $pid_ftpd $pid_ssh; do
    if ! kill -0 $pid 2>/dev/null; then
      wait $pid 2>/dev/null
      echo "Process $pid exited, cleaning up"
    fi
  done
  sleep 1
done

这个脚本启动了两个新的python脚本。trap那一句是为了在退出时把子进程也一块退出,后面的while true则是检查并自动重启两个子进程。

/tmp/.ftpd.py

#!/usr/bin/env python3
import time
import os
import setproctitle


try:
    setproctitle.setproctitle("[kworker/u8:2-events]")
except:
    pass

while True:
    os.system("echo $(date) >> /tmp/.hiddenlog")  # 模?~K~_记?~U?~U??~M?
    time.sleep(10)

这个脚本用setproctitle把自己的cmd伪装成kworker,实际执行的是不停地偷偷写日志。

/tmp/sshd.py

import os
import time

def sshd():
    pid = os.fork()
    if pid == 0:
        print(f"[Child] PID: {os.getpid()} exiting.")
        os._exit(0)
    else:
        print(f"[Parent] PID: {os.getpid()}, child PID: {pid}")
        print("[Parent] Looping forever. Child becomes zombie.")
        while True:
            time.sleep(10)

if __name__ == "__main__":
    sshd()

这个脚本不停地制造僵尸进程,主要方法为:

  • os.fork() 后立刻exit子进程,制造zombie
  • 父进程不 wait() 子进程,保持子进程为zombie
  • 父进程while true,一直不退出,防止系统回收它制造的zombie

解答

  1. What is the disguised process name (COMMAND)? 一开始我以为是entrypoint.sh的那个cmd,结果发现是[kworker/u8:2-events]。确实,只有它用了setproctitle做隐藏。
  2. What is the real command running behind it? 也是一开始以为是要用ls -l /proc/<pid>/exe来解决,但答案就是.entrypoint.sh中的那一句python /tmp/.ftpd.py

kill谁

kill -9 10会杀死python /tmp/.ftpd.py,可以通过check。不过按理说entrypoint才应该被kill掉,然而pid==1的进程是systemd,所以杀不掉。在这个docker中它在启动时就执行了恶意脚本。

截屏2025-07-29 13.59.32

task5 并行操作

  • 任务目的:了解并掌握常用的并行操作,提高代码运行效率
  • 任务要求:修改指定的代码片段的运行逻辑,使得程序运行效率大幅提示,运行时间缩短至5s以内
  • 注意事项:
    • 需要在当前目录下面复制一份template.py代码,在复制的文件上进行操作
    • 只允许修改指定范围内的代码,且不能通过硬编码的方式进行作弊

单线程

import time
import threading

total = 0
start_time = time.time()
lock = threading.Lock()

def worker(num):
    global total
    with lock:
        total += num
    time.sleep(1)
# ======= DO NOT MODIFY ABOVE =======

def main():
# ======= You may ONLY modify this section to implement concurrency =======
    for i in range(1, 16):
        worker(i)
# ====================================================================

# ======= DO NOT MODIFY BELOW =======
    end_time = time.time()
    elapsed = end_time - start_time

    print(f"Total: {total}")
    print(f"Elapsed time: {elapsed:.2f} seconds")

if __name__ == "__main__":
    main()

原有的脚本中,重复执行了15次sleep(1),所以期望完成时间为15s。

多线程

要提升性能,只需将这15个sleep(1)并行执行。可以用python自带的threading库,将main改为

def main():
    threads = []
    for i in range(1, 16):
        t = threading.Thread(target=worker, args=(i, )) # 创建线程
        threads.append(t) # 加入线程组
        t.start()

    for t in threads:
        t.join() # 使主线程等待子线程完成

截屏2025-07-29 14.09.27

task6 日志溯源

  • 任务目的:了解基本的日志溯源方法以及常见的爆破攻击
  • 任务要求:分析/var/log/auth.log日志,找出正在进行爆破攻击的攻击者IP

查看日志

直接vim,截取一部分。当然也可以cat /var/log/auth.log | grep Failed

Jul 10 11:48:45 server sshd[1234]: Failed password for invalid user admin from 45.33.32.156 port 22 ssh2
Jul 10 11:48:59 server sshd[1234]: Failed password for invalid user admin from 45.33.32.156 port 22 ssh2
Jul 10 11:49:06 server sshd[1234]: Accepted password for admin1 from 48.240.157.23 port 22 ssh2
Jul 10 11:49:58 server sshd[1234]: Accepted password for apache from 192.168.1.10 port 22 ssh2
Jul 10 11:50:43 server sshd[1234]: Failed password for invalid user backup from 193.28.93.35 port 22 ssh2
Jul 10 11:51:02 server sshd[1234]: Failed password for invalid user admin from 128.217.120.10 port 22 ssh2
Jul 10 11:51:19 server sshd[1234]: Failed password for invalid user user1 from 10.0.0.91 port 22 ssh2
Jul 10 11:51:34 server sshd[1234]: Failed password for invalid user user2 from 172.16.0.85 port 22 ssh2
Jul 10 11:51:38 server sshd[1234]: Failed password for invalid user admin from 45.33.32.156 port 22 ssh2
Jul 10 11:51:40 server sshd[1234]: Failed password for invalid user admin from 45.33.32.156 port 22 ssh2
Jul 10 11:51:56 server sshd[1234]: Failed password for invalid user admin from 45.33.32.156 port 22 ssh2

这道题的check只需要输入一个ip就可以了。

截屏2025-07-29 14.20.44

task7 Git使用

  • 访问在线靶机平台 http://gitlab.yumeeu.com:8080/
    • Nginx认证:用户名: learngit 密码: learngitlab
  • 任务要求:
    • 完成主要的所有任务,完成远程的前5个任务
    • 网页会自动弹出flag,格式为 flag{xxxxx}
  • 答案提交格式:task7_git, flag{xxxxx}

最难的一个是通过 rebase -i 修改提交历史的那个,除此之外都较简单。对于那道题,一个重要的问题是教程并没有介绍交互式变基的操作方法。实际上只需要上下拖动就好了。在终端中会进入一个编辑器,然后可以修改comment之类的。

截屏2025-07-30 14.42.52