protectai / vulnhuntr

Zero shot vulnerability discovery using LLMs
GNU Affero General Public License v3.0
1.1k stars 113 forks source link

TypeError: argument of type 'NoneType' is not iterable #14

Open hyperreality opened 4 weeks ago

hyperreality commented 4 weeks ago

I ran vulnhuntr on https://github.com/ahmedkhlief/Ninja to see its analysis of a known AFO there:

poetry run vulnhuntr -l claude -r /home/abc/Downloads/Ninja/ -v

I get the following traceback after some number of rounds of results:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/abc/Downloads/vulnhuntr/vulnhuntr/__main__.py", line 411, in run
    match = code_extractor.extract(name, code_line, files)
  File "/home/abc/Downloads/vulnhuntr/vulnhuntr/symbol_finder.py", line 48, in extract
    match = self.project_search(symbol_name)
  File "/home/abc/Downloads/vulnhuntr/vulnhuntr/symbol_finder.py", line 156, in project_search
    if 'import ' in match['source']:

The exception seems to be triggered by the first line of the context_code:

context_code:
  - name='cmd' reason='The cmd module contains the COMMANDS dictionary and potentially the implementation of command execution, which is crucial for understanding the full extent of the RCE 
vulnerability.' code_line='from core.cmd import *'
  - name='webshell.webshell_execute' reason='This function is directly called with user input in webshell mode, making it a critical point for potential RCE.' 

A small reproduction:

from vulnhuntr.symbol_finder import SymbolExtractor

ROOT = "/home/abc/Downloads/Ninja"

code_extractor = SymbolExtractor(ROOT)

extracted = code_extractor.extract("cmd", "from core.cmd import *", [f"{ROOT}/Ninja.py", f"{ROOT}/core/payloads.py", f"{ROOT}/core/cmd.py"])                                             
print(extracted)

The problem seems to be that the modules code in symbol_finder.py doesn't handle that style of import. If the first argument to extract() is changed to the full module path core.cmd then the code doesn't crash, but the if statement doesn't trigger so Jedi doesn't goto the relevant source module file.

DanMcInerney commented 3 weeks ago

I'm looking into this. Might be a complex fix.

DanMcInerney commented 3 weeks ago

I think I fixed it in my latest PR. It's now spitting out some vulns like this in Ninja:

scratchpad:
  1.Analyzed start_campaign.py for LFI vulnerabilities. 2.Identified two critical vulnerabilities in update_template() and get_ssl() functions. 3.In update_template(), 'name' variable is user-controlled and used in mkdir() and open() without sanitization.
4.In get_ssl(), user input for certificate and key paths is used without validation. 5.Both vulnerabilities allow for directory traversal and unauthorized file access. 6.Traced input from campaign_name() to update_template(), confirming the vulnerability.
7.Examined input handling in get_ssl(), confirming lack of input validation. 8.Considered potential for chained attacks due to C2 server setup context. 9.Developed PoC exploits for both vulnerabilities. 10.Evaluated impact considering the C2 server 
context, noting high severity.
----------------------------------------

analysis:
  The start_campaign.py script contains two critical LFI vulnerabilities that could lead to unauthorized file access and potential remote code execution. The first vulnerability is in the update_template() function, where the 'name' variable, derived from
user input via campaign_name(), is used directly in mkdir() and open() calls without proper sanitization. This allows for directory traversal attacks. The second vulnerability is in the get_ssl() function, which accepts user input for SSL certificate and 
key file paths without validation. Both vulnerabilities could allow an attacker to read sensitive files, write malicious files, or potentially execute arbitrary code. The script's role in setting up a C2 server amplifies the risk, as exploiting these 
vulnerabilities could compromise the entire C2 infrastructure.
----------------------------------------

poc:
  For update_template() vulnerability: 1. Input campaign name as '../../../etc/passwd' 2. This will attempt to create a directory named '../../../etc/passwd-campaign' and write to a file within it, potentially exposing sensitive information. For get_ssl()
vulnerability: 1. When prompted for SSL certificate path, input '/etc/passwd' 2. When prompted for private key path, input another sensitive file like '/etc/shadow' 3. This will attempt to read these files as SSL certificate and key, potentially exposing 
their contents.
----------------------------------------

confidence_score:
  8
----------------------------------------

vulnerability_types:
  - LFI

It is still have some trouble getting stuff like cmds.COMMANDS that I have to look into.

DanMcInerney commented 3 weeks ago

Is this the AFO?

scratchpad:
  1.Analyze upload_url route: The function accepts filename and file content from user input. 2.Examine file_path construction: filename is taken directly from user input without proper sanitization. 3.Check file writing process: decrypted file content is
written to the constructed file path. 4.Identify potential AFO vulnerability: Lack of proper input validation allows for directory traversal. 5.Construct PoC: Craft a POST request to upload_url with a malicious filename. 6.Verify vulnerability: Confirm 
that the PoC can write files outside the intended directory. 7.Assess impact: potential to overwrite critical system files or create malicious files in sensitive locations. 8.Review confidence: High confidence due to clear code path from user input to 
file writing.
----------------------------------------

analysis:
  The upload function in webserver.py contains a critical Arbitrary File Overwrite (AFO) vulnerability. The function accepts a filename directly from user input without adequate sanitization, allowing for directory traversal attacks. While there's an 
attempt to remove null bytes and split on backslashes, it's insufficient to prevent all forms of path traversal. An attacker can use '../' sequences to write files outside the intended directory. The file content is decrypted and written to the file path 
constructed using the user-supplied filename. This vulnerability could allow an attacker to overwrite critical system files or create malicious files in sensitive locations. The lack of file extension validation further exacerbates the issue. The 
vulnerability is highly exploitable due to the direct path from user input to file writing operations.
----------------------------------------

poc:
  POST /upload_url HTTP/1.1
  Host: target.com
  Content-Type: multipart/form-data; boundary=---------------------------1234567890

  ---------------------------1234567890
  Content-Disposition: form-data; name="file"

  ../../../etc/passwd
  ---------------------------1234567890
  Content-Disposition: form-data; name="d"

  ---------------------------1234567890
  Content-Disposition: form-data; name="resource"

  malicious_id
  ---------------------------1234567890--
----------------------------------------

confidence_score:
  9
----------------------------------------

vulnerability_types:
  - AFO
----------------------------------------
hyperreality commented 3 weeks ago

It is still have some trouble getting stuff like cmds.COMMANDS that I have to look into.

Yeah I wonder if part of the confusion came from the questionable coding style of the Ninja repo where cmds was used as a local variable, a module name, and a class name.

Is this the AFO?

The AFO is in the download_url route, whereas upload_url has a path traversal (because it's from the perspective of the C2 server rather than the client).

DanMcInerney commented 3 weeks ago

OK, I'll keep this open to remind me to look into Ninja's cmd.COMMANDS symbol lookup. Should be able to parse it although symbol parsing is by far the most complicated technical hurdle and getting it parse one edge case sometimes causes other edge case lookups to then fail. It's annoying.