Just Hackin'

Stories and experiences experimenting with software, malware, and cyber security

I get a text.

Kelvin, I'm moving to a new house next month and I'd like to invite you to a party at my house.

...? Am I Kelvin? Is that my name?

I responded:

Nice. 👍

What can I say, I'm a normal guy.

Their reply:

😱 What does not bad mean? i didn't understand you at all

Oh. 'Nice' and 'Not bad' are close enough that it'd mess up with an online translator. This person is not a native English speaker.

From here, I try to put up an act and a fake persona.

I correct them. My name is not “Kelvin” but “Kevin”.

(They keep calling me Kelvin.)

I send an AI generated picture of someone's face, saying that's me.

I'm invited to chat on Telegram. I join.

This person's profile is “Aileen” (the face is cut off on “her” profile pic of course) and “she” nonchalantly sends me a photo of people gathered in suits for a crypto conference.

They then ask me about crypto investments; I play along.

Eventually they linked me to a website, abercia.com, and they insisted to open it on my phone and download and install the app.

The website seems quite strange at first glance on PC, and leads you to download an .apk file if you open it on a browser with an android useragent.

I ran the file through VirusTotal and these are the results of the scan. Not much to look at, even behavioral seems pretty normal.

Even if it appears safe, I'd urge whoever reads this to avoid installing this software on their phone.

So then I decide, why not make an account and try to buy crypto?

Logged in

So I do. And then I'm greeted with this:

So then I of course need to verify my identity to buy crypto:

Ah. Real name and real driver ID number? Of course! I put in very correct (fake) information, and I see this:

Wow, fast! So next, I have to upload photos of my ID and me holding it and time of day:

So I do a very high-effort (bare minimum) version of that to get to the next screen:

This design all hurts to look at, especially the watermarking and terrible stretching of the “photos”.

I end up at a screen saying the uploaded information is pending review.

Now I'm starting to see the point behind the site...

Buying crypto

There is no actual functionality to buy crypto on the site.

Here's a page where you can, theoretically, get over 100% yearly returns in crypto:

So I click to buy, and it says to buy with USDT.

On another page to deposit, it says:

It is temporarily unavailable to use other types of coins. Please understand!

So clearly, they're an established and trustworthy exchange that lets you deposit... only USDT?

Why do they need to verify your identity if there's no functionality to connect your bank account and deposit actual money?

So I go back to “lock-in” some (nonexistent) USD-T for some nice returns.

Oh, you can't change the input field to anything other than zero.

Although, I did anyway. I just modified its value directly with a line of code.

Even then, it still says in a pop-up in the corner:

Purchase amount must be greater than the minimum

So it seemed to be hardcoded.

I spent some time looking at the actual handlers and code that launches when you click on the button – turns out it does connect to a server.

But here's the URL when I set the field to that big number:

https://www.abercia.com/v1/rest/pc/lumProject/addOrder?projectId=770210224039526400&buyCount=0

Notice how even when I manually modify the amount, it still says buyCount=0?

Conclusion

It appears this is a scam to get people's private information in order to make actual crypto accounts using their identity, and then turn around and pull money out of stolen credit cards or bank accounts, or perhaps tax refund fraud.

Clearly, the start of this is some sort of organized crime group that, texting random phone numbers, fools older gullible lonely men into giving their info into a site, effectively to steal their identity.

I'm not reporting this scam to the government since I don't want to give my personal information out and tie myself into the investigation.

If you're reading this and there really are multiple cases of the same site being used as a scam, then other people are likely being screwed over. Please report it to some agency.


Domains and IPs related to this that came about from the VirusTotal scan of the app:

  • arivaex.com
  • av1.xdrig.com
  • av1.xdrig.com.td.fusion.iaas.jdcloud.com
  • cloud.xdrig.com
  • cloud.xdrig.com.td.fusion.iaas.jdcloud.com
  • i.tddmp.com
  • me.xdrig.com
  • me.xdrig.com.td.fusion.iaas.jdcloud.com
  • 116.196.71.30:80
  • 116.198.14.128:443
  • 116.198.14.42:443
  • 52.194.64.30:443

Congrats, you figured out how to install Kali NetHunter on your phone. Now, you want to connect to it from your PC on the same network?

How NetHunter's kex VNC Script Works

The KeX program that comes with NetHunter is actually just a bash script.

kex is actually just some simple wrapping around Xtigervnc.

Try running it once, then kill it:

which kex # Shows us the location of kex, for me, that's /usr/bin/kex
cat /usr/bin/kex # Let's look inside
kex # Lets run it once, it'll make us set a password on the first run
killall Xtigervnc # End the vnc service. If you see nothing, it worked

Run a VNC Server Open to Your Local Network

Copy this one important line from that file: (This might be different on your phone – make sure to copy it from wherever which kex is!)

export HOME=${HOME}; export USER=${USR}; LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libgcc_s.so.1 vncserver $SCREEN

The important part, we're going to paste it into our terminal, but modify it by adding -localhost no to the end.

tl;dr, The Command You Want to Run:

export HOME=${HOME}; export USER=${USR}; LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libgcc_s.so.1 vncserver $SCREEN -localhost no

Troubleshooting

  • Got an error about passwords? Do vncpasswd and set it to something, then try this command again
  • Got an error about a file not found? Make sure you're in the kali shell by running nethunter first
  • Something else? Try re-reading this guide carefully, try the steps in order, and google an error if you run into one

(Note, this story is not related to – or even close to being descriptive of – any employer I've worked at, it was simply inspired by a personal friend, and honestly doesn't reflect any job I've been in, but it does remind me of a situation I've actually heard of, and I just thought this exaggeration would be funny)

drawing of tick driving car

“Well, well. If it isn't the man with a plan. Working hard and still up at...”, the tick pauses to look at its watch.

“Past 3AM!?”, it shouts in surprise.

“B-boss. I'm over here, working hard! I just wanna make you proud! This night's off company time, all on me”, the pale and shriveled up little man uttered, as he shifted uncomfortably.

“Okay... Nice work, son.” The microscopic tick, almost too small to see with the human eye, beamed with joy at its employee, a rare occurrence.

What a sucker, it thought.

Immediately, the tick regretted showing any positive emotion, remembering that an employee concerned for their job security is a productive employee.

Tch. What am I doing, smiling? Gotta keep them guessing. That's why I don't use turn signals when I change lanes. It returned to a resting frown – an expression that would reduce the effects of aging and wrinkles on it's smooth, lacquer and blood-stained exoskeleton.

Perhaps I should take the opportunity to add a fourth project on this guy's shoulders, he seems like he might actually enjoy it, thought the tick, knowing full well the bags under the man's eyes were darkening with every passing month.

The pale man stayed silent for a second, then became even more visibly awkward.

He sat up in his chair, as if to prepare himself to exorcise a painful sentence out of his extra-large breathing hole.

“Ah? Well, I hope you keep up the good performance”, said the tick, beginning to contort the upper half of its polished husk away from the man.

“Wa-wait, pl-please. If you have a minute, sir.”

Oh dear god. The tick's mind began to race. He's going to say he got a job offer and wants to leave. Of course. I've been working him too hard, putting on too much pressure, and now I'm losing one of my most productive assets. I'll tell him he can't leave, that it's going to complicate his 401k situation, that his wife and kid are going to be put in financial trouble if that new company starts doing budget cuts.

Aha, I'll remind him the economy is bad. When isn't it bad when you make under $40k? Plus, he can't really ever leave after working here for so many years, his heart isn't in it. Bite into him. Tell him we have a place for him here, that he belongs. Wait. Does he even have a wife and kid? Isn't this guy in his late 40s and living in a rental apartment more than an hour away? No way, that's right. He's single and has been here for at least 5 years. He'll do whatever I say.

“Now, listen her-”, the tick stops, realizing it's entirely forgotten the name of this employee. Was it... Curtis? Clyde? ...Richard? “Yes, s-sir?”, the jaunt-faced man stuttered. “No, go on...”, urged the tick. “W-well. I've b-been at the company f-for two decades... and...”

Holy crap. Who in their right mind would limit their career prospects like that after 2020? This guy is so painfully loyal and gullible, I'm so ready to reel him back in once he tells me he's leaving. He's younger than me and he's already balding, I'll bring that up when I tell him there's two kinds of people in this world: leaders, and followers.

Baldness really breaks down their ego, makes them crawl back to you. Lets you latch onto them for longer, before they give out. Worst case, I'll guilt trip him into a 6 week notice. After that, they might drop him at the new place, and he might stay here after all. Really ram it into him for that next month and a half that he's gotta stay.

“A-and... so... I wa-was hoping... you could find it in your heart... to give me a promotion! Oh god, I'm sorry... I hope I didn't upset you”, the fragile man pleaded in a dry, raspy voice.

Wow. How can I even try to fathom this shadow of a man?

Permanently helpless. Trapped forever in a rat race. This is exactly the kind of person I want to stay in their exact role forever and never leave. I have to keep him here for the rest of his working life. No, past that. Until he draws his last breath.

“Maybe I should remind you that people are retiring later and later nowadays, things are getting rough in the economy. You shouldn't be making demands like that at a company you've been at for only 20 years.” Throw him against the wall. And then... suck what little bit of light and life behind those eyes are left, until he feels like giving up, and becomes a machine for me.

The tick continued, “You see, your life is going very well. You should be grateful. This company has given you... so, so much.”

The man makes a wide, wrinkled frown as his eyes begin to water.

Oh shit. He's going to cry. I overestimated his resolve, his character, what little sense of individuality he had left. He's already shattered, and he was trying to pick up the pieces, and his hands are covered in blood from all the cuts into his soul. His delicious, succulent blood. Let me reel this back in. Make him smile. Get his hopes up, make some shit up.

“And... you've been a loyal employee. I respect a man who works to fill in the shoes of a short-staffed team of seven men down to one. Maybe I'll even consider it, in a year or two”, the tick says.

Psh. Yeah, in a year or two, the tick thinks to itself, when I'm going to scalp him from this company, after I leave with him to make my own startup. After he builds out the whole product, he'll be so hysterically confused when venture capitalists come in and lay him off to cut down on costs.

I'll be off having exited shortly before, landing with my golden parachute onto a yacht filled with 47 virgins like those funny cube worshippers dream they'd have.

The man has a glow of happiness come over him. A wide smile of joy not often seen in that office spreads across his face.

“S-sir! Thank-”, he stops to sniffle, “...thank you for y-your consideration!”

What a stupid grin. Shit, ignorance is bliss. It's almost like this is one of the most fulfilling moments of his life.

He stood up for himself once in his life, asked for something, got an empty promise, and still...! That's better than anything he's experienced in probably years. This man does not fuck. This man has no standards, no ambition, no motivation in life but to slave away at this goddamn company for scraps. His happiness is derived from the praise and reactions of his superiors, who'd fire him without a day's notice.

He deserves to be at the bottom of the hierarchy. This is why classes exist. He cannot understand or handle success, wealth, or status. He must be led. He must be shepherded. He must be consumed, when the time of slaughter comes and he grows old. I am his shepherd. I lead him to his death.

The man is still beaming, smiling, exuberantly. “I'll stay all night to finish this sir, you can count on me!”

No, he doesn't even realize I'm a dictator in his life, that I'm controlling his financial well-being. Employees really think they're the ones in control, somehow. They don't realize the situation they're in at all is that of a parasite and a host.

I am a tick, slowly drinking away at his blood, taking everything out of him, sucking him dry for what he's got.

He's compromising with a tick he found on himself.

Ever have services suddenly break and realize after running df -h that you're out of disk space?

Fixes to common problems:

# this one can clear **gigabytes** of space
sudo purge-old-kernels -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --allow-downgrades

# this will remove packages not in use
sudo apt-get autoremove -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --allow-downgrades

# this will remove snapd package cache
sudo rm -rf /var/lib/snapd/cache/*

Packages

In general, a good recommendation is to set up a script that runs some commands to cleanup packages every week or so:

export DEBIAN_FRONTEND=noninteractive
sudo apt update

# This long command will automatically choose to keep existing files and install all upgrades on your machine
# NOTE: This is the most risky command as it'll keep you updated and on the bleeding edge if you run it on schedule - make sure you know what you're doing (and the risks) if you use this in production environments
sudo apt-get upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --allow-downgrades

purge-old-kernels -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --allow-downgrades

# This will remove dependencies not installed/in use
sudo apt-get autoremove -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --allow-downgrades

# Delete unnecessary cache for snapd packages
sudo rm -rf /var/lib/snapd/cache/*

Services

One place that lots of logs can accumulate – journald logs. You can clean them up pretty easily by using the CLI arguments to remove old logs:

sudo journalctl --vacuum-size=5M

Don't this manually in the future, instead open up the journald config and set a limit on how much space can be used.

Normally, this line in /etc/systemd/journald.conf is commented out, but just remove that comment and set a reasonable log filesize limit:

SystemMaxUse=5M

Then do sudo systemctl restart systemd-journald.service, and now it should stay under 5 megabytes in size.

Misc

General advice: look for logs, file uploads, things that endlessly grow and might never be cleaned up or removed.

One command to show the 30 largest files and folders just in the current directory:

du -cks * | sort -rn | head -30

A command to show the 30 largest individual files under the current directory:

find . -printf '%s %p\n'| sort -nr | head -30

(Courtesy of TutorialsPoint)

 

Docker & Docker-Compose

If you use docker, make sure to create/edit a file called /etc/docker/daemon.json and make sure it has this attribute in it:

{
    "log-driver": "none"
}

If you use docker-compose, set your configs to turn off logging by setting the driver to none for all docker-compose files' services:

services:
  website:
    image: nginx
    logging:            # <--- add this
      driver: none      # <---

Alternatively, set a limit:

services:
  website:
    image: nginx
    logging:
      driver: "json-file"
      options:
        max-size: "512m"

UFW

If you use UFW, you'll see it generate a lot of useless logs. Run ufw logging off to completely turn off logging.

Particularly with VSCode, you might run into issues with having a working version of rls (stands for Rust Language Server).

You might see this:

# rustup component add rls

error: component 'rls' for target 'x86_64-unknown-linux-gnu' is unavailable for download for channel 'nightly'
Sometimes not all components are available in any given nightly. If you don't need the component, you can remove it with:

    rustup component remove --toolchain nightly --target x86_64-unknown-linux-gnu rls

Basically, nightly doesn't always have a version of rls that is supported.

Explanation From the Devs

It explains the problem on the official rls repo:

The development of rustc's internals is quite fast paced. Downstream projects that rely on nightly internals, particularly clippy, can break fairly often because of this. When such breakages occur the nightly release will be missing rls. [...] rustup will warn if the update is missing any components you currently have. This means you can no longer accidentally update to a no-rls release. Once rls is available again it'll update.

Solution

Check out https://rust-lang.github.io/rustup-components-history/. Choose your rust target in the dropdowns below.

Specifically look at the “Last available” column. That'll be the date that the package was available – the rls row would be the one to pay attention to.

Using that date, replace the letters in this command:

rustup install nightly-20YY-MM-DD # ex: rustup install nightly-2022-04-13

Then switch to that as the default and then install those packages on nightly:

rustup default nightly-20YY-MM-DD
rustup component add rls rust-analysis rust-src

That fixed it for me, so hope that helps!

Now your #![feature(type_ascription)] statements and others should work just fine with IDE debugging support!

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.

Python

Simple inline execution of commands, without semicolons:

__import__("os").system("ping 192.168.1.1")

Alternatively, using subprocess:

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

Executing from shell, using complete import:

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

Perl

From repl:

exec "/bin/sh";

From shell:

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

Ruby

From repl:

exec "/bin/sh";

From shell:

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

Lua

From repl:

os.execute('/bin/sh')

From shell:

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

Awk

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.

:!sh

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.

This is a bit of a hack, but you can make HTTP behave in a stateful way.

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.

Well, 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.

TCP connections start with three handshake packets, then data goes between client and server, and importantly, the connection ends with a FIN packet.

The idea is this: By leveraging the fact that a website can keep it's connection to the user's 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.

You can do this 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

I created 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, one can independently submit HTTP requests, and the server can update/add to the main <iframe>, which hasn't disconnected yet and is still waiting for the signal to close the connection.

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 receiving and never-closing <iframe> 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.

Issues

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 http://127.0.0.1:8000; # 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.

Impact

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.

References

[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