Testing AppSec rules with POST body contents
I'm testing whether my AppSec component blocks in case one of the rules of the virtual patching collection is being hit. I'm using CVE-2024-29824 as an example: https://app.crowdsec.net/hub/author/crowdsecurity/appsec-rules/vpatch-CVE-2024-29824. According to the rule a POST request to a URL ending in
/wsstatusevents/eventhandler.asmx
and containing xp_cmdshell
should trigger the rule. When I'm simulating such a request, it's not blocked, though.
As far as I know this is an in-band rule, so it should react immediately. I have successfully tested with GET /rpc2
, so the AppSec component itself seems to be functioning. POST requests that only consider headers being set seem to be result in the expected response too.
When I check the (debug) logs I see this:
It seems the arg=
indicates the body that is evaluated is empty, but I haven't set disable_body_inspection: true
. Is there something I'm missing?CrowdSec Console
Hub Appsec rule
Use CrowdSec Console to visualize security data, manage dynamic blocklists, and gain real-time intelligence on IPs. Enhance your threat response capabilities.
16 Replies
Important Information
Thank you for getting in touch with your support request. To expedite a swift resolution, could you kindly provide the following information? Rest assured, we will respond promptly, and we greatly appreciate your patience. While you wait, please check the links below to see if this issue has been previously addressed. If you have managed to resolve it, please use run the command
/resolve
or press the green resolve button below.Log Files
If you possess any log files that you believe could be beneficial, please include them at this time. By default, CrowdSec logs to /var/log/, where you will discover a corresponding log file for each component.
Guide Followed (CrowdSec Official)
If you have diligently followed one of our guides and hit a roadblock, please share the guide with us. This will help us assess if any adjustments are necessary to assist you further.
Screenshots
Please forward any screenshots depicting errors you encounter. Your visuals will provide us with a clear view of the issues you are facing.
© Created By WhyAydan for CrowdSec ❤️
Hey,
Do you forward all the request headers to crowdsec (on top of the specific appsec headers) ?
We use coraza behind the scenes, and unfortunately, to properly parse some request body, it needs the content-type header (we've made some modification to expose the body as a giant string if coraza cannot parse it, )
(although, the rule matches on RAW_BODY so that should not be an issue :/)
I've just tested with our nginx bouncer, and it seems to work:
I had a quick look at your PR in the bouncer, I don't see anything obviously wrong with the way you pass the request to crowdsec
the only thing I see right now to improve would be to add some configuration to prevent forwarding huge bodies to crowdsec, as it can consume tons of memory and will be very slow:
the idea would be to allow the user to configure a max body size to forward and what to do if the body is bigger than the limit:
- drop the request
- only send query string + headers
(we don't do it currently in the nginx bouncer, but it's planned, and I think we'll document this in the protocol reference for the appsec to make use everybody does it)
Yeah, that's a sensible configuration to add. I'll likely iterate on what I currently have, and the nice thing is that Caddyfile parsing makes adding things fairly simple and clean.
This is an example dumping the request, using your
curl
example:
It does seem to match on the path, but the body doesn't match:
Still looks empty in that args=
, which I think is where that xp_cmdshell
should be
Does that turn up in your AppSec (debug) logs when you do it with Nginx?I do see the body in the log:
do you see the body if you add some debug in the bouncer ?
some webserver might treat the body a bit differently and not always make it available straightaway
This is what the curl request looks like with
httputils.DumpRequestOut
:
From this I don't see a content length header so I guess go (crowdsec side) is ignoring the body, oh wait it chunked so it doesn't need it
good catch @iiamloz
I've just tried using chunked encoding, and nginx did not block anything
So we need to find out if it's an issue on the bouncers side or something in crowdsec
the nginx bouncer seems to read the body just fine, so this seems to be an issue in crowdsec
but that's weird, AFAIK, go should handle this automatically for us
@hslatman can you try with this PR: https://github.com/crowdsecurity/crowdsec/pull/3342 ?
Should fix the issue (I still need to test it a bit more thoroughly to make sure it did not break something else)
GitHub
appsec: better handle chunked requests by blotus · Pull Request #33...
We were relying on the content-length header to compute the size of the buffer we need to allocate to store the body, but in the case of chunked requests, the content length is not set, thus we wer...
Hey @blotus, didn't have time over the weekend due to some personal stuff, but I'll have a look at it. When I set the content length it does pass the POST tests I made, so it's indeed something related to that.
I suppose the Docker images from the build of that branch aren't published and pullable like the regular ones, right?
no we don't build images on PRs
You'll need to checkout the branch and build it yourself
Hey @blotus, I tested your patch last week, and that did the trick. For now I've changed it on the client side by explicitly setting the content length. There's already the need to read the incoming request, so it's not that big of an issue to have to set it. I could change that in the future to default to not setting it, but before I do so I think it would be nice if there was a way to determine which version of the LAPI / CrowdSec is running from the bouncer's viewpoint. But maybe I'm overthinking, and people should just update their CrowdSec.
I don't think we expose the version of LAPI to the bouncers
But i guess we could return it as a header in all (authenticated) LAPI/appsec responses (I'll discuss with the team, but I don't see any drawback)
Yes, I was thinking about something like that 🙂
Alternatively it could go (just) in
/heartbeat
, I suppose
Or is that not intended to be used from a bouncer? It's not in the OpenAPI spec, so maybe not accessible?the heartbeat endpoint only supports credentials authentication (ie, log processors) (as we recommend to run bouncers in stream mode, we have a heartbeat for "free")
Right, yeah; with streaming it's fine, and I've basically been using it a bit like that with support for "failling hard" when the connection fails, and otherwise returning/logging an error. But my bouncer also supports live mode, and for that it would be nice to see if the connection details for the LAPI are OK. I suppose a HEAD request to some endpoint, for example the decisions endpoint, would be OK?
yes, a HEAD request on
/decisions/stream
will work fine (we won't update anything in the DB)