Coordinated Disclosure Timeline
- 2021-05-13: Report sent to maintainers
- 2021-05-14: Report bounced
- 2021-05-14: Created GitHub issue asking for maintainer’s contact information
- 2021-11-22: Created GitHub issue warning of the expiration of the disclosure deadline
- 2021-11-24: Disclosing as per our disclosure policy.
Summary
An authenticated attacker can execute arbitrary commands on the system.
Product
Gerapy
Tested Version
0.9.6
Details
Issue 1: project_clone
The function project_clone
is vulnerable to command injection while handling attacker controlled data. The address
(1) variable is used in the creation of a git clone
command (2) in an insecure way that allows an attacker to craft the url of a repository that contains shell commands (3).
From views.py:
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def project_clone(request):
"""
clone project from github
:param request: request object
:return: json
"""
if request.method == 'POST':
data = json.loads(request.body)
# NOTE(1): Address comes from the post's body.
address = data.get('address')
if not address.startswith('http'):
return JsonResponse({'status': False})
address = address + '.git' if not address.endswith('.git') else address
# NOTE(2): Address is used to build a command without sanitization.
cmd = 'git clone {address} {target}'.format(address=address, target=join(PROJECTS_FOLDER, Path(address).stem))
logger.debug('clone cmd %s', cmd)
# NOTE(3): Command is executed.
p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = bytes2str(p.stdout.read()), bytes2str(p.stderr.read())
logger.debug('clone run result %s', stdout)
if stderr: logger.error(stderr)
return JsonResponse({'status': True}) if not stderr else JsonResponse({'status': False})
Example:
By specifying address
to be /dev/null || malicious code #
the executed command will look like this:
git clone /dev/null || malicious code #.git <...>
Issue 2: project_parse
The function project_parse
is vulnerable to command injection while handling attacker controlled data. Attacker controlled data coming from the body of a POST
request (1) is used in the creation of a shell command (2) in a way that allows an attacker to execute arbitrary commands on the host (3).
From views.py:
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def project_parse(request, project_name):
"""
parse project
:param request: request object
:param project_name: project name
:return: requests, items, response
"""
if request.method == 'POST':
project_path = join(PROJECTS_FOLDER, project_name)
# NOTE(1)
data = json.loads(request.body)
logger.debug('post data %s', data)
spider_name = data.get('spider')
args = {
'start': data.get('start', False),
'method': data.get('method', 'GET'),
'url': data.get('url'),
'callback': data.get('callback'),
'cookies': "'" + json.dumps(data.get('cookies', {}), ensure_ascii=False) + "'",
'headers': "'" + json.dumps(data.get('headers', {}), ensure_ascii=False) + "'",
'meta': "'" + json.dumps(data.get('meta', {}), ensure_ascii=False) + "'",
'dont_filter': data.get('dont_filter', False),
'priority': data.get('priority', 0),
}
# set request body
body = data.get('body', '')
if args.get('method').lower() != 'get':
args['body'] = "'" + json.dumps(body, ensure_ascii=False) + "'"
# NOTE(2)
args_cmd = ' '.join(
['--{arg} {value}'.format(arg=arg, value=value) for arg, value in args.items()])
logger.debug('args cmd %s', args_cmd)
cmd = 'gerapy parse {args_cmd} {project_path} {spider_name}'.format(
args_cmd=args_cmd,
project_path=project_path,
spider_name=spider_name
)
logger.debug('parse cmd %s', cmd)
# NOTE(3)
p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
stdout, stderr = bytes2str(p.stdout.read()), bytes2str(p.stderr.read())
logger.debug('stdout %s, stderr %s', stdout, stderr)
if not stderr:
return JsonResponse({'status': True, 'result': json.loads(stdout)})
else:
return JsonResponse({'status': False, 'message': stderr})
Impact
Code Execution
CVE
- CVE-2021-32849
Resources
- https://github.com/Gerapy/Gerapy/issues/197
- https://github.com/Gerapy/Gerapy/issues/217
Credit
This issue was discovered and reported by @RasmusWL (Rasmus Wriedt Larsen) from the CodeQL Python team.
Contact
You can contact the GHSL team at securitylab@github.com
, please include GHSL-2021-076
in any communication regarding this issue.