SadServers 通关记录

本文最后更新于 2023年3月26日 凌晨

“Like LeetCode for Linux”

SadServers 通关记录

关于 SadServers

  • SadServers 是一个 SaaS,用户可以在真正的 Linux 服务器上以类似“CTF”的方式测试他们的 Linux 运维技能。

  • 为了减少地球二氧化碳排放,节省作者的 AWS 账单,一旦完成题目,建议使用 sudo shotdown -h now 关闭服务器

Troubleshoot and make a sad server happy!

通关记录

“Saint John”: what is writing to this log file?

  • 题目描述:某个测试程序连续写入日志文件 /var/log/bad.log 并填充磁盘。找到并终止它。

  • 解答

1
2
3
4
5
# 查找占用 `/var/log/bad.log` 进程的 pid
lsof |grep /var/log/bad.log

# 终止进程
kill -9 [pid]
  • 检测
1
2
# 检测终止进程后是否在 6 秒内被修改过
find /var/log/bad.log -mmin -0.1

“Saskatoon”: counting IPs

  • 题目描述:根据Web服务器访问日志 /home/admin/access.log 找出请求最多的 IP 地址,并将其写入 /home/admin/highestip.txt

  • 解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 取出文件第一列
awk '{print $1}' /home/admin/access.log

# 排序
awk '{print $1}' /home/admin/access.log |sort

# 计数(次数在第一列)
awk '{print $1}' /home/admin/access.log |sort |uniq -c

# 对计数排列
awk '{print $1}' /home/admin/access.log |sort |uniq -c |sort -r

# 取第二列第一行
awk '{print $1}' /home/admin/access.log |sort |uniq -c |sort -r |head -1 |awk '{print $1}'

# 输出到指定文件
awk '{print $1}' /home/admin/access.log |sort |uniq -c |sort -r |head -1 |awk '{print $1}' > /home/admin/highestip.txt
  • 检测
1
2
# 检验 hash 值
sha1sum /home/admin/highestip.txt

“Santiago”: Find the secret combination

  • 题目描述:

    1. 找到 /home/admin 目录中字符串 Alice*.txt 文件中的出现次数
    2. 在字符串 Alice 仅出现一次的文件中在此之后一行中的数字
    3. 将这两个数字写入 /home/admin/solution
  • 解答:

1
2
3
4
5
6
7
8
9
10
11
# 找字符串出现次数(-type f 指普通文件),返回各文件中字符串出现次数
find /home/admin -type f -name "*.txt" |xargs grep -c 'Alice'

# 写入结果(无换行符)
echo -n 411 > /home/admin/solution

# 对仅出现一次的文件 1342-0.txt 查找 alice 出现后的一行
cat /home/admin/1342-0.txt| grep 'Alice' -A 1

# 写入结果(追加一行)
echo 156 >> /home/admin/solution
  • 检测
1
2
# 检验 hash 值
md5sum /home/admin/solution

“Manhattan”: can’t write data into database

  • 题目描述:现有 Postgres 数据库中无法插入,解决该问题。

    • Postgres 监听 5432 端口,data_directory 在 /opt/pgdata/main,由 systemd 作为一个名为 postgresql 的单元进行管理
  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 重启服务
sudo systemctl restart postgresql

# 查看服务错误日志,postgresql cluster 启动失败
journalctl -p err

# 查看系统日志,发现空间不足
cat /var/log/syslog

# 查看磁盘空间,/opt/pgdata/ 占用满
df -h

# 检查 data_directory 目录大小
du -sh /opt/pgdata/main

# 删除 /opt/pgdata/ 中所有 *.bk 文件并重启
rm /opt/pgdata/*.bk
sudo systemctl restart postgresql
  • 检测
1
2
# 插入数据
sudo -u postgres psql -c "insert into persons(name) values ('jane smith');" -d dt

“Tokyo”: can’t serve web file

  • 题目描述:配置好的 Web 服务器,代理 /var/www/html/index.html 时异常

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 测试 curl 命令未中止且无回显,说明网阻塞
# 查看防火墙规则,http 阻塞
iptables -L

# 清空防火墙
iptables -F

# 测试 curl 命令有回显,显示 403 Forbbiden
# 查看权限
ls -l /var/www/html/index.html

# 修改权限
chmod 777 /var/www/html/index.html
  • 检测
1
curl 127.0.0.1:80

“Cape Town”: Borked Nginx

  • 问题描述:执行 curl -I 127.0.0.1:80 时回显 curl -I 127.0.0.1:80 returns curl: (7) Failed to connect to localhost port 80: Connection refuse。解决该异常。Nginx 由 systemd 管理。

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 重启 Nginx,失败
systemctl restart nginx

# 检查配置文件,报错
nginx -t

# 重新加载配置并重启 nginx,检测报错 500 Internal Server Error
# 查看 Nginx 日志,发现 24: Too many open files
cat /var/log/nginx/error.log

# 增加 systemd 中在 `/etc/systemd/system/nginx.service` 的打开文件上限 LimitNOFILE
# 重新加载 systemd 配置并重启 nginx
systemctl daemon-reload
systemctl restart nginx
  • 检测
1
curl -Is 127.0.0.1:80| head -1

“Salta”: Docker container won’t start

  • 问题描述:在 8888 端口上启动一个用于 Node.js 应用的容器,容器镜像目录在 /home/admin/app

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查看列表容器
docker ps -a

# 查看启动日志,server.js 缺失
docker logs 124a4fb17a1c

# 修改 Dockerfile
cd /home/admin/app
vim Dockerfile

# 重新构建镜像
docker build -t app .

# 启动容器,发现端口占用
docker run -d -p 8888:8888 app

# 查看端口占用(lsof -i:[port] 无法使用,并解除占用后启动成功
netstat -tunlp| grep 8888
kill -9 620
  • 检测
1
curl localhost:8888

“Venice”: Am I in a container

  • 问题描述:判断是在容器中还是在宿主机中

  • 解答:

1
2
3
4
5
# 查看 PID = 1 进程的环境变量,有 container=youlooked
cat /proc/1/environ|tr "\0" "\n"

# 查看根目录信息 inode 编号(宿主机的根目录的 inode 编号一般为 2,与平台有关)
ls -ali
  • 检测:无

“Oaxaca”: Close an Open File

  • 问题描述:文件 /home/admin/somefile 已打开以供某些进程写入。在不终止进程的情况下关闭此文件。

  • 解答:

1
2
3
4
5
# 获取文件描述符编号,77
lsof /home/admin/somefile

# 关闭文件描述符编号 77 的文件
exec 77>&-
  • 检测
1
2
# 无返回
lsof /home/admin/somefile

“Melbourne”: WSGI with Gunicorn

  • 问题描述:

    • /home/admin/WSGI.py 是一个Python WSGI web应用程序文件,其目的是提供字符串“Hello,world!”。
    • 该文件由 Gunicorn 服务器提供,该服务器由 nginx 服务器前置(两个服务器均由 systemd管理)。
    • 因此,HTTP请求的流程是:Web Client(curl)->Nginx->Gunicorn->wsgi.py
    • 目标是配置服务器使得 curl -s http://localhost 时返回“Hello,world!”。
  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 返回 Connection refused
curl localhost

# nginx 代理 /run/gunicorn.socket
cat /etc/nginx/sites-enabled/default

# gunicorn 使用的 /run/gunicorn.sock,修改 nginx
cat /etc/systemd/system/gunicorn.service

# 无回显
curl localhost

# 返回 Content-Length: 0,修改 wsgi.py(使用等于或大于消息所需的13个字节数,或完全去掉Content-Length 标头)
curl -i localhost

# 重启
systecm restart gunicorn
  • 检测
1
curl -s http://localhost

“Lisbon”: etcd SSL cert troubles

  • 问题描述:在 https:// localhost:2379 上运行了一个 etcd 服务器,获取键“foo”的值

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
# certificate has expired or is not yet valid(系统时间超前一年)
etcdctl get foo

# 设置系统时间到一年前
date -s "last year"

# 查看防火墙
sudo /usr/sbin/iptables -t nat -L

# 删除规则
sudo /usr/sbin/iptables -t nat -F
  • 检测
1
etcdctl get foo

“Jakarta”: it’s always DNS

  • 问题描述:在 ping google.com 时返回 ping: google.com: Name or service not known

  • 解答:

1
2
3
4
# 配置解析顺序
# 把 hosts: files 修改为 hosts: files dns
# 代表先 files(/etc/hosts)解析,再从 dns(/etc/resolv.conf)解析
vim /etc/nsswitch.conf
  • 检测
1
ping google.com

“Bern”: Docker web container can’t connect to db container

  • 问题描述:一个正常的 Wordpress Docker 无法了解到 MariaDB Docker,curl -s localhost:80 |tail -4 时显示 Error establishing a database connection

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 查看 Wordpress 容器与 WORDPRESS_DB 相关的配置
docker exec wordpress env | grep "WORDPRESS_DB"

# 查看 MariaDB 容器相关的配置
docker inspect mariadb

# 比较 wp-config.php,username 和 password 正确,但是 WORDPRESS_DB_HOST 未定义
docker exec wordpress grep WORDPRESS_DB_ /var/www/html/wp-config.php

# 能够正常访问
docker exec wordpress mysqladmin -h mysql -u root -ppassword -h 172.17.0.2

# 利用 --link 重建 docker
docker stop wordpress ; \
docker rm wordpress ; \
docker run -d \
--name wordpress \
-v html:/var/www/html \
--link mariadb:mysql \
-p 80:80 \
-e WORDPRESS_DB_HOST=mysql \
-e WORDPRESS_DB_NAME=wordpress \
-e WORDPRESS_DB_USER=root \
-e WORDPRESS_DB_PASSWORD=password \
wordpress:sad
  • 检测
1
2
# 返回 mysqld is alive
sudo docker exec wordpress mysqladmin -h mysql -u root -ppassword ping

“Karakorum”: WTFIT – What The Fun Is This?

  • 问题描述:一个二进制文件 /home/admin/wtfit 没法正常启动,试修复之

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Permission denied
/home/admin/wtfit

# /usr/bin/chmod: Permission denied
chmod +x /home/admin/wtfit


# 在无法使用 chmod 的情况下授予执行权限
# 通过 perl 脚本调用
perl -e 'chmod 0755, "/usr/bin/chmod"'

# 通过 ld-linux 库调用
/lib64/ld-linux-x86-64.so.2 /usr/bin/chmod +x /usr/bin/chmod


chmod +x /home/admin/wtfit

# ERROR: can't open config file
/home/admin/wtfit

# strace 检查调用,发现打开 /home/admin/wtfitconfig.conf
strace /home/admin/wtfit

# 创建 /home/admin/wtfitconfig.conf
touch /home/admin/wtfitconfig.conf

# ERROR: can't connect to server
/home/admin/wtfit

# 再次检查调用,发现使用 127.0.0.1:7777
strace /home/admin/wtfit

# 使用 netcat 监听端口,接收到一个 HTTP GET 包
nc -l -p 7777

# 启动 HTTP 服务器
python3 -m http.server --bind 127.0.0.1 7777
  • 检测
1
2
# 返回 OK
/home/admin/wtfit

“Singara”: Docker and Kubernetes web app not working

  • 问题描述:从主机访问部署的“webapp”网络服务器并找到它服务的消息

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 获取所有 pod 状态,web 状态为 ImagePullBackOff
kubectl get -A pod

# 尝试 pull webapp 镜像失败
docker pull webapp

# 查看所有镜像,有 register
docker images

# 启动 register 镜像仓库
docker run -d -p 5000:5000 registry:2

# 提交镜像
docker tag webapp localhost:5000/webapp
docker push localhost:5000/webapp

# 删除 webapp-deployment 的 Deployment 对象
kubectl delete deploy webapp-deployment -n web

# 修改 deployment.yaml,需要改 image 为 localhost:5000/webapp 以及 port 为 8888
vim /home/admin/deployment.yml

# 应用修改
kubectl apply -f /home/admin/deployment.yml

# 开放端口
kubectl port-forward deployments/webapp-deployment 8888 -n web
  • 检测
1
curl localhost:8888

“Hong-Kong”: can’t write data into database

  • 问题描述:类似于 “Manhattan” 问题,需要插入一行到 Postgres 数据库中

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 查看服务错误日志,显示 Timed out waiting for device /dev/xvdb
# postgres 的目录在 /opt/pgdata/ 但没数据,可能有未挂载的磁盘卷
journalctl -p err

# 查看磁盘分区状态,/dev/nvme0n1 未挂载
lsblk -f

# 尝试挂载,失败
mount /dev/nvme0n1 /opt/pgdata

# 查看日志,显示 Unit is bound to inactive unit dev-xvdb.dev
journalctl|tail

# 修改 /etc/fstab,把 /dev/xvdb 改成 /dev/nvme0n1
vim /etc/fstab

# 重启,挂载成功
systemctl daemon-reload
mount /dev/nvme0n1 /opt/pgdata

# 执行仍失败,日志显示 No space left on device
# 检查磁盘空间,/opt/pgdata/ 占用满
df -h

# 计算 Postgres 数据目录大小
du -sh /opt/pgdata/main

# 删除 /opt/pgdata/ 中所有 *.bk 文件并重启
rm /opt/pgdata/*.bk
sudo systemctl restart postgresql
  • 检测
1
2
# 返回 insert 0 1
sudo -u postgres psql -c "insert into persons(name) values ('jane smith');" -d dt

“Pokhara”: SSH and other sshenanigans

  • 问题描述:一个用户 client 和它的 SSH 公钥被添加到服务器。目标是能够作为该用户使用它的 SSH 密钥在本地进行 SSH(只有一台服务器)。

  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 直接运行,报 “man-in-the-middle” 警告
sudo -u client ssh client@localhost 'pwd'

# 清空 known_hosts 配置
ssh-keygen -f "/home/client/.ssh/known_hosts" -R "localhost"

# 再次运行,显示 client@localhost: Permission denied (publickey).
sudo -u client ssh client@localhost 'pwd'

# 删除 ssh 错误配置并重启服务
rm /etc/ssh/sshd_config.d/sad.conf
systemctl restart ssh

# 再次运行,显示 WARNING: UNPROTECTED PRIVATE KEY FILE! Permissions 0644 for '/home/client/.ssh/id_rsa' are too open.
sudo -u client ssh client@localhost 'pwd'

# 修改 /home/client/.ssh/id_rsa 权限
chmod 600 /home/client/.ssh/id_rsa

# 再次运行,显示 Your account has expired; please contact your system administrator.
sudo -u client ssh client@localhost 'pwd'

# 查看 client 的账户有效期,被设置到 0
lslogins client
# 或
grep client /etc/shadow

# 修改有效期
chage -E-1 client

# 再次运行,显示 exec request failed on channel 0,系统线程有限制
sudo -u client ssh client@localhost 'pwd'

# 查看文件描述符配置(/etc/pam.d/sshd 中引用),删除 client hard nproc 0
vim /etc/security/limits.conf

# 再次运行,显示 This account is currently not available.
sudo -u client ssh client@localhost 'pwd'

# 查看用户登录信息,shell 显示 /usr/sbin/nologin
lslogins client

# 查看所有可用 shell
cat /etc/shells

# 设置用户登录 shell 为 /bin/bash
usermod --shell /bin/bash client
  • 检测
1
2
# 返回 /home/client
sudo -u client ssh client@localhost 'pwd'

“Roseau”: Hack a Web Server

  • 问题描述:说明:本地 Apache Web 服务器可以提供的文件中存储了一个 secret。 找到这个 secret 并将其保存为 /home/admin/secret.txt 文件。

    • 密码破解程序 Hashcat 和 Hydra 是从软件包安装的,而 John the Ripper 二进制文件是从 /home/admin/john/run 中的源代码构建的
  • 解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 有 webfile 文件,权限不够无法打开
ls /var/www/html/

# 401 Unauthorized,需要正确的 credentials
curl localhost

# 查看 credentials 位置,在 /etc/apache2/.htpasswd
vim /etc/apache2/sites-enabled/000-default.conf

# 用 John the Ripper 破解,得到账户:密码为 carlos:chalet
cd ~ ; john/run/john /etc/apache2/.htpasswd

# 查看 webfile,并输出到 secret
curl localhost/webfile -u "carlos:chalet" --output secret

# 查看 secret 文件类型,是 zip
file secret

# 解压,但是有密码保护
unzip secret

# zip2john 提取 hash
john/run/zip2john secret > zip.hash

# 用 John the Ripper 破解,得到密码 andes
john/run/john zip.hash

# 解压,成功得到 secret.txt
unzip secret
  • 检测
1
2
# 返回 cc2c322fbcac56923048d083b465901aac0fe8f8
sha1sum /home/admin/secret.txt |awk '{print $1}'

参考资料


SadServers 通关记录
https://justloseit.top/SadServers 通关记录/
作者
Mobilis In Mobili
发布于
2023年2月2日
更新于
2023年3月26日
许可协议