Skip to content

New feature for web form module: HTTP response codes, and a big cleanup#63

Open
martijnf1 wants to merge 16 commits into
jmk-foofus:masterfrom
martijnf1:feature-web-form-module-response-code
Open

New feature for web form module: HTTP response codes, and a big cleanup#63
martijnf1 wants to merge 16 commits into
jmk-foofus:masterfrom
martijnf1:feature-web-form-module-response-code

Conversation

@martijnf1

Copy link
Copy Markdown

I am a Dutch security tester and c programmer and stumbled over a limitation of the web-form module: Not being able to handle different HTTP status codes, specifically redirects. I decided to implement that functionality: If a status 301, 302, 307 or 308 is encountered then the Location header is read, and the requested resource is updated to match it's value. The request is then sent again.

The code was rather bloated, so i refactored it quite a bit and documented parts of the code while i worked on it. I also removed some deprecated functions such as index() and replaced unbounded string functions with bounded ones wherever possible.

I have used my new version succesfully, but i have not tested it in depth. I think we would all benefit if some people reviewed and tested my code as well before merging this to the master branch.

Martijn Fleuren added 4 commits March 8, 2024 16:01
handling for for 301,302,307 and 308 which are all redirects. The
default behaviour is to retrieve the resource specified at the Location
header and continue with the response from that.
readable than before. Some static testing verified that it works... but
some more testing should be done to make sure.
…string because it's hidden under a function call
@martijnf1 martijnf1 marked this pull request as draft March 8, 2024 20:44
@martijnf1

Copy link
Copy Markdown
Author

A shower-thought struck me: We're explicitly passing a Connection: Close header here, which may be required... If we're able to keep the connection open, especially in ssl sessions, this would cut all the key exchanges and tcp handshakes out of the equation which would then raise the number of logins/min

@jmk-foofus

Copy link
Copy Markdown
Owner

Hi @martijnf1

Thanks for the pull request. Unfortunately, my ability to review and test this is limited. I have not historically used the web-form module, nor do I have a good environment to thoroughly test it in. However, I did notice a couple of items:

  • Listing modules ("medusa -d") now results in a double free due to the free added at line 965.
    + vnc.mod : Brute force module for VNC sessions : version 2.1
    + web-form.mod : v[92][EF]X[05]
double free or corruption (fasttop)
  • summaryUsage with the OPENSSL_WARNING was moved outside of the HAVE_LIBSSL else block. As such, if the double free is fixed, it now reports incorrectly that OPENSSL is missing.
    + web-form.mod : Brute force module for web forms : version 3.0 (No usable OPENSSL. Module disabled.)

I no longer use Medusa on a daily basis. However, I do expect to tag a release in the next week to get the SMBv2 bits I worked on out there. I'm happy to merge this in if you get those items cleaned up and additional testing on your end looks good.

Joe

Martijn Fleuren added 2 commits March 25, 2024 10:38
… as missing while the module is successfully compiled with openssl support
…ng modules (medusa -q) shows a consistent message. solved double free by simply removing the free and making the caller responsible for freeing the pointer, which is what it was anyway. My bad.
@martijnf1

Copy link
Copy Markdown
Author

Hi,

Thanks for reviewing. I've addressed the problems:

  • The openssl warning message, including space and parantheses are now part of a conditional static string, so that the warning message is correctly formatted /and/ correctly displayed.
  • Removed the free at the end of summaryUsage(), which makes the caller responsible for freeing that pointer. This is the way it was before anyway.

@jmk-foofus

Copy link
Copy Markdown
Owner

My initial testing is showing some issues. For example, against a simple Airsonic install:

./medusa -M web-form -n 8080 -h 192.168.10.10 -u admin -p admin -v 99 -w 99 -m FORM:/airsonic/login -m FORM-DATA:"post?j_username=&j_password=&_csrf=5efb72d6-fca9-4df1-b5e6-c327a3827a58&submit=Log+in" -m DENY-SIGNAL:Incorrect -m CUSTOM-HEADER:"Cookie: JSESSIONID=4BB07CD420D9B9F7E0CCC3DD08DAEABA"

I'm getting this error when trying to free parameters at line 787:

DEBUG MODULE [A8E1C6C0]: [web-form.mod] HTTP Response code was 302.
DEBUG MODULE [A8E1C6C0]: [web-form.mod] Following redirect.
DEBUG MODULE [A8E1C6C0]: [web-form.mod] Sending Web Form Authentication (POST)
free(): invalid size

It's also doing a POST on the redirect, when it'd need to be a GET for the testing to actually work.

@martijnf1

Copy link
Copy Markdown
Author

Thanks for adressing any problems you encounter, i will look into this later in the week.

For response codes i've followed the specification on the RFC 9110, it is stated there that for 301 and 302, the request MAY be changed from POST to GET, for 307 and 308 this behaviour is forbidden. Changing the method from POST to GET means that POST-body data is lost, because according to the same spec, it shoud be ignored in GET requests. i'll assume that is commonplace.

Martijn Fleuren and others added 8 commits April 2, 2024 17:14
…rtijnf1/medusa into feature-web-form-module-response-code
	1. Typos in comments
	2. newModuleData() function initialises all members of the struct to
		 either 0 or NULL, which is implicit in the calloc, therefore the
		 explicit assignments are removed.
	3. Added break statements to parseHttpStatusCode. This is just for
		 sanity because the status codes i put there are actually not
		 implemented i.e. for future extension. Functionality does not
		 change
	4. Removed getLocationHeaderValue to not make the code overly
		 specific.
	5. Removed the check that tests whether the number of arguments that
		 is given is between 0 and 5. It is nonsensical to test only the
		 number of arguments given since i can provide only CUSTOM-HEADER
		 arguments, or give a valid set of arguments with 10 CUSTOM-HEADERs
		 which will make the test fail.
	6. Made testing for NULL values explicit. `if (x)` and `if (x !=
		 NULL)` are equal but the latter is more clear and makes sense from
		 a type-perspective.
	7. GET_REQUEST_FMT_STR had an erroneous extra "%s" in the format
		 string which caused GET requests to be mangled
	8. The Location header can return relative or absolute paths. For now
		 i made a simple distinction between the two and concatenate a
		 relative path onto the old path, so path resolution is `outsourced'
		 to the server. This is not elegant but it'll do, for now.
	- HTTP_UNAUTHORIZED was spelled wrong,
	- HTTP_MOVED_PERMANENTLY was not included in the status code
	  parser.
	- the _findHeaderValue function has been rewritten to coop with
	  a wider range of Location header formatsm mostly including
	  whitespace.
	- an initialised flag has been added to moduledata so that
	  expensive string functions are skipped after the first
	  initialisation
	- Following a redirect changes requests type from POST to GET,
	  but restores it to POST on successive requests by using an
	  additional flag.
	- Noted that urlencode DOES NOT work with unicode text, this
	  medusa plugin is only compatible with ascii. using unicode
	  WILL crash the application because mallocs will fail deep
	  down.
	-
	- Instead of checking whether a path is relative or absolute,
	  try to interpret the actual type in a separate fuction.
	- A new module state, MSTATE_INITIALIZE, has been added. This
	  state is for code that is only supposed to run once, instead
	  of every run. This replaces the moduleData->initialised flag
	  that i put in earlier.
	- the connect() method (medusaConnect or medusaConnectSSL) is
	  chosen only once, instead of every cycle.
	- Fixed an error in the default switch(nState) case where the
	  state was not set to MSTATE_EXITING after encountering an
	  unknown module state.
is.

        - POST to GET redirections set cookies, so the reponse has to be
          searched for Set-Cookie headers and those cookies have to be
          set.
        - Removed a few superfluous strdups to make the code more
          efficient.

The code is very heap-intensive and a lot of allocations happen on every
cycle. it's probably best to address this in the future by reserving a
single block of memory that is large enough for all subsequent requests
and rebuild the request string there.
different file because it became too large, and maybe it is useful to
other modules as well. It seems to work for all cases that i've been
able to throw at it. I'd like some testing feedback.

The speed of these path resolutions with my new code are ~120M/s on a
single core of a high end dell laptop. so it is not likely to be a
bottleneck.
@martijnf1

Copy link
Copy Markdown
Author

Could you rerun your test with my additions? I'm reasonably certain that it should work now.

@martijnf1 martijnf1 marked this pull request as ready for review October 14, 2024 13:38
@jmk-foofus

Copy link
Copy Markdown
Owner

I realize it's been a year since I've looked at this...

The example from above still fails out. In this case, it looks like the Location: returned in the 302 isn't sent on the next request (i.e., "?error" is omitted).

DEBUG [41C006C0]: Data sent: POST /login HTTP/1.1[0D][0A]Host: 127.0.0.1:8080[0D][0A]User-Agent: I'm not Mozilla, I'm Ming Mong[0D][0A]Connection: close[0D][0A]Cookie: JSESSIONID=F8F195E3A2932E87322F8832D88B1B7C[0D][0A]Content-Type: application/x-www-form-urlencoded[0D][0A]Content-Length: 90[0D][0A][0D][0A]j_username=admin&j_password=admin&_csrf=04c30ae9-2543-457f-a70c-c1cd049e3cce&submit=Log+in
DEBUG MODULE [41C006C0]: [web-form.mod] Retrieving server response.
DEBUG [41C006C0]: Data receive: Data waiting.
DEBUG [41C006C0]: Data received (498): HTTP/1.1 302 [0D][0A]Vary: Origin[0D][0A]Vary: Access-Control-Request-Method[0D][0A]Vary: Access-Control-Request-Headers[0D][0A]Set-Cookie: remember-me=; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/[0D][0A]X-Content-Type-Options: nosniff[0D][0A]X-XSS-Protection: 1; mode=block[0D][0A]Cache-Control: no-cache, no-store, max-age=0, must-revalidate[0D][0A]Pragma: no-cache[0D][0A]Expires: 0[0D][0A]X-Frame-Options: SAMEORIGIN[0D][0A]Location: http://127.0.0.1:8080/login?error[0D][0A]Content-Length: 0[0D][0A]Date: Tue, 06 May 2025 02:04:19 GMT[0D][0A]Connection: close[0D][0A][0D][0A]
DEBUG [41C006C0]: Data received (0): 
DEBUG [41C006C0]: Data receive: No additional data.
DEBUG [41C006C0]: Formatted data received (size 498): HTTP/1.1 302 [0D][0A]Vary: Origin[0D][0A]Vary: Access-Control-Request-Method[0D][0A]Vary: Access-Control-Request-Headers[0D][0A]Set-Cookie: remember-me=; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/[0D][0A]X-Content-Type-Options: nosniff[0D][0A]X-XSS-Protection: 1; mode=block[0D][0A]Cache-Control: no-cache, no-store, max-age=0, must-revalidate[0D][0A]Pragma: no-cache[0D][0A]Expires: 0[0D][0A]X-Frame-Options: SAMEORIGIN[0D][0A]Location: http://127.0.0.1:8080/login?error[0D][0A]Content-Length: 0[0D][0A]Date: Tue, 06 May 2025 02:04:19 GMT[0D][0A]Connection: close[0D][0A][0D][0A]
DEBUG MODULE [41C006C0]: [web-form.mod] HTTP Response code was 302.
DEBUG MODULE [41C006C0]: [web-form.mod] Following redirect.
DEBUG MODULE [41C006C0]: [web-form.mod] Changing request method to GET for redirect
DEBUG [41C006C0]: Disconnect successful
DEBUG [41C006C0]: Connected (internal)
DEBUG MODULE [41C006C0]: [web-form.mod] Sending Web Form Authentication (GET).
DEBUG [41C006C0]: Data sent: GET http://127.0.0.1:8080/login HTTP/1.1[0D][0A]Host: http://127.0.0.1:8080/login[0D][0A]User-Agent: I'm not Mozilla, I'm Ming Mong[0D][0A]Connection: close[0D][0A]Cookie: JSESSIONID=F8F195E3A2932E87322F8832D88B1B7C[0D][0A]Cookie: remember-me=; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/[0D][0A][0D][0A]
DEBUG MODULE [41C006C0]: [web-form.mod] Retrieving server response.
DEBUG [41C006C0]: Data receive: Data waiting.
DEBUG [41C006C0]: Data received (917): HTTP/1.1 400 [0D][0A]Content-Type: text/html;charset=utf-8[0D][0A]Content-Language: en[0D][0A]Content-Length: 762[0D][0A]Date: Tue, 06 May 2025 02:04:19 GMT[0D][0A]Connection: close[0D][0A][0D][0A]<!doctype html><html lang="en"><head><title>HTTP Status 400 [E2][80][93] Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 [E2][80][93] Bad Request</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).</p><hr class="line" /><h3>Apache Tomcat/9.0.60</h3></body></html>
DEBUG [41C006C0]: Data received (0):```

There are also a handful of compiler warnings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants