Just Hackin'

Experimenting with software, malware, and cyber security

For when you forget or don't know how to take advantage of an eval() statement or spawn a shell in a specific language or escape some common programs.


Simple inline execution of commands, without semicolons:


Alternatively, using subprocess:

__import__('subprocess').run(["ls", "-l"])

Executing from shell, using complete import:

python -c 'import os; os.system("ls -l")'


From repl:

exec "/bin/sh";

From shell:

perl —e 'exec "/bin/sh";'


From repl:

exec "/bin/sh";

From shell:

ruby -e 'exec "/bin/sh"'


From repl:


From shell:

lua -e 'os.execute("/bin/sh")'


awk 'BEGIN {system("/bin/sh")}'

Escaping jails and restricted shells

Trying desperately to escape a restricted shell? Refer to the guide at https://fireshellsecurity.team/restricted-linux-shell-escaping-techniques/

Escaping more

This can be anywhere from some script being printed as an MOTD before disconnecting you during an SSH session, to something more subtle like a paged viewer being run with elevated privileges.


Forcing systemctl, man, etc, to display with more

If you set the $SYSTEMD_PAGER or $PAGER env variable to more, when paged data is printed in a terminal, you can shrink your terminal to force more to show you only a portion of text rather than all text, letting you escape to shell with :!sh.

Spawning a shell with Nmap

You might be thinking, “What?” But in restricted shells, this can come in handy.

root@kali:~$ nmap --interactive
nmap> !sh

Nothing is working!!

Ran out of options? Maybe what you need isn't to escape a shell, but rather to escalate privileges. Try using Security Sift's LinuxPrivChecker.py tool, which does a lot of the tedious work for you and gives suggestions at the end of the script on what exploits might work.

Still stuck? If all else fails, check out g0tmi1k's blog post on privilege escalation.

Lately, I've been learning about the vague and nebulous thing called hacking more, over on CTF sites and the like.

Sometimes, I'll run into an obstacle when trying to solve something.

I hear this is common. But do you know the symptoms of hacker's block?


After basic attempts at finding more clues fail, and initial pathways to the solution all hit what seem like dead ends, the puzzle appears to grow in difficulty with the passage of time.

You may experience the following symptoms after a few hours pass with zero meaningful progress:

  • Accumulation of sweat droplets on the forehead
  • Music exhausts rather than motivates
  • The mind wanders away from the puzzle

After many hours or days with no progress whatsoever:

  • Torschlusspanik; anxiety and fear that time is running out in life
  • All attempts to think of new ideas bring a sense of dread
  • Mild physical fatigue, tense posture
  • Disappointment in self in the face of failure

You may find yourself unable to do anything but stare blankly at the screen and repeat your previous attempts to avail.

But it doesn't have to be like this.


The most effective technique known to science is to... change your feel.

Most people view the whole CTF thing through the lens of a competitive game. This is fun, and can be motivating for some. Typically, a fun and challenging game involves doing something repeatedly and not giving up until you get through the enemy or obstacle.

The only issue is you may not be well-equipped to pass or defeat the foe, and it isn't as easy as going back to grinding on weaker enemies and leveling up.

Hacking is different – after enumeration and scanning, you usually have to think creatively rather than try to bash your way through with brute force (even if that may work in some situations, and in most cases when attacking vulnerable systems in real life).

Some challenges will require you to question the very nature of what you know, and think about a certain thing differently from how you normally do.

The optimal learning situation:

  1. You have a target or puzzle and you enumerate and investigate it as needed.
  2. You come up with some solutions – some fail, but you continue to think of new things to try, hopefully with some serious effort required.
  3. Every once in a while you learn something new and make progress.
  4. You manage to solve the puzzle while remaining mostly engaged the entire time.

You feel happy and motivated, and you move up in difficultly as you feel comfortable with more of a challenge.

The “Throes of Perdition” situation:

  1. You no longer make any progress or you haven't made any actual progress for a long time, and it feels like you have absolutely nothing else to go on – you've hit every dead-end you could find.
  2. You take shots in the dark over and over in the hopes that you'll figure out what else there is to the puzzle.
  3. You begin to feel like you're guessing/fuzzing, desperately trying to find something, anything, without end in sight.
  4. Bags form under your eyes from stress and your wrists ache more than usual. You have not been drinking water or eating well while painfully struggling to make progress.
  5. You begin to hear voices tell you it would be nice to become a mediocre normal human being, and Consume Entertainment™ as somesuch escapist distractions beckon you to them.

God, please, end the pain!

When it feels like you're stuck and have absolutely no idea what to try, and you feel like you've exhausted all of your options long ago, DON'T keep going endlessly into the abyss.

Give yourself a mental break, go do something else. Switch challenges, learn about something in the same vein as the challenge, or ask a friend around the same level as you what they would do in your scenario.

By dwelling on the problem and what you currently know and what information you have at the moment, you are very likely closing yourself into repetitive thoughts.

You probably aren't addressing the root cause; there is something you don't know that you have yet to figure out. In this state, your mind begins to fog over like an old grandmother's front lawn on Halloween (those fog machines were expensive – should've invested the money in SpaceX instead).

A desperate hacker's clouded mind:

  • You try to think of a new idea or something else to attempt.
  • You think about the same things you've tried before, and how they didn't work for a certain reason.
  • You try to think of everything that you currently know, and again, try to think of another idea.
  • You quickly scan through your old ideas that didn't work once more, fiddle with a few things on your desk, type ls in a terminal in front of you to collect your thoughts, and adjust yourself in your chair.
  • You zone out for what feels like 2 minutes. You come to, and notice the time is much later than you had hoped. You try googling something from earlier a little bit differently, but no luck.
  • You try to think of a new idea again.

This can keep happening for a long time. Maybe even a long, long time.

Just remember what you started doing this for, and think about whether or not it's worthwhile to continue going this way without gaining any new knowledge or skill.

There are better things you could be doing with your time. There are endless resources online that can help you get closer to your end goals, this isn't your only option to improve.

It'd be better to revisit difficult and unsolved problems with fresh eyes and a clear mind than battering away for days at the same problem.

That's not to say that sometimes it may make sense – and even be a necessary step in developing the hard-earned grit and determination that is required to be truly 1337 – but in most cases, you shouldn't beat yourself up over what you don't know, and move on.

You might already know that the HTTP/HTTPS protocol is stateless, and that we need cookies and JavaScript to have an interactive experience on the web with sessions, where our browsers need to either:

  • constantly establish new connections for each request with AJAX
  • use a persistent connection like websockets to have a stateful data exchange between client and server


Well, I think I figured out a new (or is it old?) way that doesn't rely on JavaScript to have live data updates on a webpage.

The Workaround

While HTTP isn't supposed to be treated as stateful, it is based on top of the TCP protocol, which is stateful.

Here is a simple TCP connection, and also how an HTTP connection starts:

  1. Client ------SYN-----> Server
  2. Client <---SYN/ACK---- Server
  3. Client ------ACK-----> Server
  4. (Application data is sent back and forth)
  5. Client <-----FIN------ Server (simplified)

The idea is this: By leveraging the fact that a website can keep it's connection to the browser open forever, it can keep sending more and more data to the user. By never closing the connection, you can keep the client waiting and update the HTML without a new connection required.

How, you ask, could this do anything useful?

The way I thought of was by using several <iframe> tags. This lets us concurrently do something in one <iframe> while another is used to submit data to the server, allowing the other to receive more HTML and update, live.

Still unsure of practical applications for this technique? Keep reading.

Proof-of-Concept live chat with zero JavaScript

Recently, I finished a project called ZeroChat. The idea is that, by never closing the server's response and letting the browser continue to wait for more HTML, you can continue sending more data; hence, keep showing more messages.

If you have <iframe>s in the initially loaded page, they can independently submit HTTP requests, and the server can update/add to the main page, which hasn't disconnected yet and is still waiting for the signal to close the connection.

Imagine this example in your head: – Make the user load a single page with two <iframe>s inside. – Display a form in the first <iframe> which will submit and lead back to itself after submitting. Keep in mind this is all happening inside the first <iframe>. – Display a page in the second <iframe> which never receives a final packet from the server. Never finishing the HTTP load from the server, this <iframe> will keep getting more and more messages from other people in the chat and see their messages get added to <iframe>'s body.

Yes, this works. Imagine your page constantly spinning as if it hasn't finished loading, and when you click on a button in an <iframe> to send your chat message, you submit a POST request to the server.

The server sends the other <iframe> with more HTML upon receiving this request, and there you have it, a (hacky) solution that provides stateful HTTP.

You can host it yourself pretty easily, ZeroChat is just a bit of NodeJS. All you have to do is clone it locally and do npm install; npm start; and visit http://localhost:8000. There you go, a live chat with zero client-side scripts.


Main problem I ran into while I was proxying the requests through Nginx (this didn't happen with Apache) was that the reverse proxy would buffer the data from the server to the client, preventing the live updates on their browser. The solution is to use proxy_buffering off; in the location {...} block.

Here's an example for googlers looking for a quick fix:

server {

        server_name chat.example.com;

        location / {
                proxy_buffering off; # Fixes the issue!
                proxy_pass; # ZeroChat server running locally on port 8000

        listen 80;

In the past, browsers like Firefox would slow down significantly if the connection lasted for a long time, but now both Firefox Quantum and Chrome work splendidly at sustaining long-term HTTP connections that never close.

The only current issue is that Firefox doesn't handle more than one concurrent <iframe> that loads endlessly. If you have multiple, Firefox will just get stuck and not show anything in either until one stops loading, even though it works fine in Chrome. I personally like Firefox a lot, so I wanted support in my chat for both, so I'm only keeping a single <iframe> permanently open.

If Firefox also supported multiple streaming <iframe>s at the same, then we could have more things update, like a live up-to-date sidebar with a list of online users. Actually, that one example can still be achieved, but it would be quite clunky and involve absolute positioning or floating.

One more thing – Chrome stays autofocused on a child <iframe>'s input fields if the <iframe> was in focus previously and gets reloaded. Firefox doesn't do this, so you have to click/tap on the input box to type a message every time you send one.

Maybe someone reading this could talk to the Firefox devs and ask them to implement this to be on par with Chrome? I might create a new issue about if nobody else does, but it's sortof a non-issue if you don't care about this.

What were you thinking?!

I was inspired by a number station I found on a Tor hidden site once, which kept endlessly sending more and more mp3 data without reloading the page, back when JS was blocked completely on the Tor Browser. I was amazed at how it worked, and sought to replicate it.

I accidentally stumbled upon this technique because I didn't think it shouldn't have been possible – my understanding of web servers and back-end programming at the time was very... infantile, for the first implementation. I think it was that naiveness which allowed me to think outside the box.

I know I'm probably not the first person to think of this, but I haven't found anyone else online who's done such a thing before.

EDIT: Looks like I finally found a mention of this exact concept back in 2009, someone even stating that they were asked about it in an interview!

Out of nowhere, a Bluetooth RCE vulnerability for Android was surreptitiously patched January 2019.

As far as I can tell, it was an ethical disclosure to Google that happened sometime before or during early November 2018.

It didn't garner much attention from anyone besides a few blog posts online that only briefly mention how the vulnerability is of critical nature.

What surprised me more than anything is that, considering how severe this vulnerability is, and how it can allegedly be exploited in person when close enough for Bluetooth radio, how nobody has done a write-up on it or investigated the vulnerability online yet.

I looked deeper into this and got the diffs from the source code before and after the fix to the vuln.

Here is the actual function in question:

And the only place where the function gets called is within this branch:

As you can see, there isn't a lot that was changed. Besides what appears to be some renamed constants, the main difference is a change in the way the size of the number at the bta_ag_parse_cmer is checked, using now a passed in parameter instead of utilizing a function that converts the string to an int.

This is a common occurrence, but if done incorrectly can result in logic susceptible to an integer overflow attack.

Still investigating this, will update further when I have the chance.


All versions of Android 7 though 9 are affected, meaning a large slice of the Android user base is vulnerable – worst part is, Android phones bought from manufacturers or carriers almost never get updated after the first few years.

If this ever gets picked up by bad actors, it could be a very interesting toolkit that might be used in worm-like attacks that spread like a disease throughout the whole world.

One car entertainment system or kiosk at a mall with Bluetooth on can get infected, and then infect any devices nearby that are vulnerable with a copy, and so on, to the point where a massive botnet propagated by Bluetooth could be under control by a single bad actor.

Reminds me of the BlueBorne vuln, a very similar vulnerability that got a lot more press. Not sure why this didn't land on anyone's radar.


[1] https://nvd.nist.gov/vuln/detail/CVE-2018-9583#vulnCurrentDescriptionTitle

[2] https://android.googlesource.com/platform/system/bt/+/b90b669eb40316f13df9f080add09c29139f4d3a/bta/ag/bta_ag_cmd.c