lab7 server
lab7 服务器使用
- 选填题
- docker
- task0 签到
- task1 符号链接
- task2 资源
- task3 ps
- task4 恶意进程
- task5 并行
- task6 日志
- task7 git
选填题
选择
-
sudo
添加用户、进入root需要sudo,挂载文件系统需要使用mount命令访问/dev/sdX,也需要sudo。
-
可疑用户
攻击者往往用僵尸网络来爆破,账号密码简单。
-
编辑器
vim和nano是常见编辑器。awk也可以生成新文件,但是cat只能查看不能编辑。
-
后台运行
tmux、nohup和screen都可以,&则依赖于当前的shell,断开ssh就没了。
-
抓包工具
Nessus是漏洞扫描工具。Wireshark是GUI,tcpdump是CLI,Burp Suite专精Web。
填空
-
pypi
管理python包的最方便方式还是虚拟环境,用conda环境随便装。另外还可以备份环境避免玩坏了。
-
查看端口
相比于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
-
进入task0_login目录
cd /opt/tasks/task0_login
-
运行check文件
./check
-
获取ID和flag
- 该题目为签到题,没有设置检查点,运行后可直接获取flag
使用的服务器是我的Web服务器,通过内网访问。


task1 符号链接
- 任务要求:通过执行命令使得文件夹
/home/student/data和/mnt/examdata保持相同的读写行为 - 完成要求之后,运行task1_file目录下面的check文件获取flag
- 注意事项:在你执行正确的命令之前,确保/home/student/data目录是不存在的,否则check可能会失效
题解
这实际上就是创建符号链接。
ln -s /mnt/examdata /home/student/data

task2 资源查看
- 任务目的:了解常用的查看资源的命令与方法
- 任务要求:分别执行至少两种不同的命令用于查看内存、磁盘、CPU使用情况。
- 注意事项:运行
check之前,需要提前运行history -w命令确保你执行的命令被记录
内存
# 快速查看内存情况
free -h
# 更详细
vmstat -s
磁盘
# 查看挂载的整个文件系统
df -h
# 查看某个目录
du -sh /home
CPU
# 动态显示各种用量
top
htop
# 查看每个核的用量
mpstat -P ALL 1 1

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

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
解答
- What is the disguised process name (COMMAND)? 一开始我以为是entrypoint.sh的那个cmd,结果发现是[kworker/u8:2-events]。确实,只有它用了setproctitle做隐藏。
- 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中它在启动时就执行了恶意脚本。

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() # 使主线程等待子线程完成

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就可以了。

task7 Git使用
- 访问在线靶机平台 http://gitlab.yumeeu.com:8080/
- Nginx认证:用户名: learngit 密码: learngitlab
- 任务要求:
- 完成主要的所有任务,完成远程的前5个任务
- 网页会自动弹出flag,格式为 flag{xxxxx}
- 答案提交格式:task7_git, flag{xxxxx}
最难的一个是通过 rebase -i 修改提交历史的那个,除此之外都较简单。对于那道题,一个重要的问题是教程并没有介绍交互式变基的操作方法。实际上只需要上下拖动就好了。在终端中会进入一个编辑器,然后可以修改comment之类的。
