Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

martijnf1
Copy link

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.

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
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
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

… 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
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
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
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.

martijnf1 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
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
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