Rob Vinson

Drupal SQLi CVE-2014-3704

Sure the SSLv3 CBC padding oracle attack dubbed POODLE is a big deal, any attacks that break SSL are for sure a big deal. However this attack as described requires MiTM. As such its probably not going to cause the instantaneous doom that the media is trumpeting. In fact it seems to be overshadowing another more tactile threat. Specifically the unauthenticated SQLi vulnerability in Drupal core that was announced yesterday (CVE-2014-3704 | SA-CORE-2014-005). Drupal is a pretty commonly deployed CMS, the amount of folks this could impact is probably pretty high. So naturally last night I started to look at the changes introduced to Drupal to patch this issue, and to figure out how it will be exploited. Though when coming back to it this evening I see an exploit has been posted on packetstorm. Early bird gets the worm and all that jazz… Anyway, since I took some time to investigate this vulnerability last night I may as well blog some details.

funky foreach

The commit just makes a slight change to the expandArguments() function. The comments for this function indicate that it expands out array arguments into a SQL query. The fix makes sure that a php foreach statement only iterates over the values of the array by using the array_values() function. That seems a bit odd until you realize that foreach has two forms. One iterates over values, and one that iterates over key –> values.

Boring iteration over values
1
2
3
4
5
// form one
$arr = array("one", "two", "three");
foreach ($arr as $value) {
      echo "Value: $value\n";
}

The above code will simply assign each array element in $arr in turn to the variable $value then print it out. You can try it yourself at [http://writecodeonline.com/php/] if you’d like. Nothing groundbreaking here… The next form is a bit more interesting.

Slightly less boring iteration
1
2
3
4
5
6
7
8
$arr = array("one", "two", "three");
foreach ($arr as $key => $value) {
      echo "Key: $key => Value: $value\n";
}
// Output: 
// Key: 0 => Value: one
// Key: 1 => Value: two 
// Key: 2 => Value: three 

Ok, so the above foreach will assign the array element’s index to $key and the value to $value, fair enough. This is the sort of array that the vulnerable expandArguments() function was expecting. Though as it turns out PHP arrays are really maps. So these “arrays” can have key > value elements like you would expect from a hash/dict/map in some other languge. This leads us to the final foreach example..

Last foreach example I promise
1
2
3
4
5
6
7
8
$arr = array("a" => "one", "b" => "two", "c" => "three");
foreach ($arr as $key => $value) {
      echo "Key: $key => Value: $value\n";
}
// Output: 
// Key: a => Value: one
// Key: b => Value: two 
// Key: c => Value: three 

Ah ha! So that’s why the fixed foreach statment added in array_values(). They want to make sure someone hasn’t slipped in a key value pair. This becomes more evident when you look through the rest of expandArguments() and see that the key actually makes its way into a query.

expandArguments snippet
1
2
3
4
5
6
7
// ...snip...
foreach ($data as $i => $value) {
// ...snip...
  $new_keys[$key . '_' . $i] = $value;
}
// ...snip...
$query = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $query);

So, now we know what we need to aim for, getting an array with a key value pair to this function, and putting our SQLi payload into the key.

Finding the path to vulnerable code

We know to exploit this we need to get an array we can control to expandArguments(). To investigate help this I:

  • installed Drupal 7.31 on a VM.
  • turned on MySQL query logging by adding the line “log = /var/log/mysql/all.log” to /etc/mysql/my.cnf.
  • instrumented the expandAruments() function by using fopen() to open a log file and fwrite() to write out the key/values being handled by the function.

After browsing through the Drupal code a bit to figure out how a HTTP GET/POST request could result in an appropriately formed array making its way to expandArguments() I found the function drupal_http_build_query. Reading this code and running a few test arrays through the function via [http://writecodeonline.com/php/] was sufficient to get a feel for the function.

arrays in URLs
1
2
3
4
5
6
7
8
9
10
11
12
$a = array("a", "b", array("x","y","z"));
foreach($a as $k => $v) {
  echo "$k => $v\n";
};
echo "encoded array: "
echo drupal_http_build_query($a);

// Output:
// 0 => a
// 1 => b
// 2 => Array
// encoded array: 0=a&1=b&2[0]=x&2[1]=y&2[2]=z

From the example above We now know that you can set a key in an array by using brackets in a Drupal URL. Using burp to capture a login attempt (it doesn’t matter that username and password are wrong) and then using burp repeater to edit the login request and replay it, forcing an error that showed SQLi didn’t take long. One could probably construct a request file to be used with sqlmap and go to town now.

Alas, since someone just released an exploit I’ve lost interest…

XSS Honor System

Saw this in the wild. XSS honor system, too funny…

Lantronix Serial-to-ethernet

Serial-to-Ethernet Devices

I’ve come across various serial-to-ethernet adapters over the years. What’s a serial-to-ethernet adapter? They allow you to plug in some device that communicates information via serial (E.G. RS-232) into an IP network. For example, maybe you have some temperature sensor shares out readings via a serial interface. You hook this sensor into an IP network via a serial-to-ethernet adapter. VoilĂ , now you can get your readings from your desktop, server, etc.

Lantronix

Recently I’ve crossed paths with some Lantronix serial-to-ethernet adapters.

Some notable things that stand out on these devices:

  • Web-based configuration has a blank default username/password
  • Telneting to 9999/TCP offers up another means of configuration, again no password is necessary by default.
  • 30718/UDP runs a service by which information about the device can be queried, and configuration may be done.

30718/UDP

There are many functions available over 30718/UDP. After some some time googling, I discovered a zip file containing documents which detail these functions.

  • Node reset (03)
  • Firmware version query (F6)
  • Response to firmware version query (F7)
  • Setup record query (F8)
  • Response to setup record query (F9)
  • Set configuration – supplying a setup record (FA)
  • Response to set configuration (FB)
  • Set IP address (FC)

So, the device can be reset, configured, and have its IP address changed all via UDP, huh…

Particularly funny is that response to a setup record query has a field for “telnet config password”. If I’m understanding correctly, if 9999/TCP has a password, you can just look up the password over 30718/UDP.

Scanning for Lantronix Devices

My Metasploit scanner module can be found at robvinson/metasploit-modules on GitHub.

What a scan looks like:

1
2
3
4
5
6
7
8
9
msf > use auxiliary/scanner/scada/lantronix_discover
msf  auxiliary(lantronix_discover) > set RHOSTS 10.0.0.0/24
RHOSTS => 10.0.0.0/24
msf  auxiliary(lantronix_discover) > run

[+] Found 10.0.0.15 - Lantronix Device type: UDS-10/Cobox 4.X
[+] Found 10.0.0.112 - Lantronix Device type: XPort-03/04
[*] Scanned 256 of 256 hosts (100% complete)
[*] Auxiliary module execution completed

Update – 01/30/2013

Looks like some others stumbled across lantronix and there are metasploit modules! One extracts the telnet password, and one grabs the version from a telnet banner.

Ubiquitous Web Server

Web servers on everything…

Vendors put web servers on damn near everything. You can find some pretty interesting stuff out there running web servers if you just start looking for it. Serial to TCP/IP adapters, cameras, HVAC stuff… folks plug all kinds of these kinds of things into IP networks given the chance. Often enough, these devices have a web server running that you can visit to configure the device, monitor its status, etc.

Sometimes they have passwords set

Sometimes these sorts of devices have the ability to set a password on them. If that’s the case, there’s a good chance whoever installed the device didn’t bother to change the password from the default. Someone can just search for the manual/documentation for the device to find out if there are default passwords for it. The installers probably think, “Why bother setting a decent password on this thing? I’ll just forget it. There’s no sensitive information stored on this thing anyway, it just supports some instrument.”

Analogy time: I don’t keep my life savings in my car. That doesn’t mean I’m cool with someone opening my car door, getting in, and screwing around with it. Also, I’m not inclined to leave my car parked in a bad neighborhood for too long. I need my car; I depend on it.

Here comes the rocket science

Put a decent password on your network connected stuff! Also, you probably don’t really need to plug it into that accessible network in the first place. Give it a private IP if you can, or segregate it into a network for devices of its kind.

People make mistakes sometimes

Yeah, people make mistakes, some don’t know any better, and others are hopeless.

Here’s a trick

  1. Do a port scan across a range of IPs to find open web services (port 80/tcp)
Discover web servers
1
2
nmap -PN -p80 -T4 --max-retries 1 10.0.0.0/16 -oG port80scan
grep '80/open' port80scan.gnmap | awk '{print $2}' > port-80-open-ips.txt
  1. Use wget to pull down the index page for each IP address and store those in files. (download dl.sh)
Download the pages(download.sh)
1
./dl.sh port-80-open-open-ips.txt
  1. grep across the files for things that could be interesting
Find goodies
1
grep -i 'serial' 80open/*

Dotfiles

Keeping in sync

I have a bash shell running all of the time, and Vim running most of the time. It only makes sense to customize and personalize them a bit. Usually I’ll customize my prompt, and maybe add an alias or two here and there. After happening across some Vim configuration tip that sounds useful, I’ll open up my .vimrc and toss it in. Over time the .bashrc and .vimrc on my laptop and desktop evolve into their own beasts. Certain commands or aliases will only be available on one of the systems I use, but not others.

Recently I’ve gotten tired of manually syncing up the customizations that I really care about between my systems. So, I’ve decided to try to clean up my configs, and stick them in a git repository. The idea is, whenever I add a new alias, I just need to git push it to the repository. To sync up another system I only have to git pull in the changes. This isn’t a new idea.

So what?

Why don’t I use someone else’s dotfiles repository? Simple, l want mine personalized to my needs/wants/whims. I can still use others’ repositories for inspiration…

General structure

The general idea is to store files like .bashrc, .vimrc, etc. in a directory. I named mine dotfiles. The layout follows:

dotfiles/
dotfiles/bash
dotfiles/bash/plugins
dotfiles/vim
dotfiles/vim/autoload
dotfiles/vim/bundle

There is a _bashrc file under the bash directory and a _vimrc file under the vim directory. The .bashrc and .vimrc files will then be symbolic links the respective files in the dotfiles directory. This idea can be extended to whatever other initialization dotfiles you’d like to keep in your repository.

Bash plugins directory

This directory lets me make my bash customizations a bit more organized. For instance, I have a coding file under that directory that sets up aliases I use for get and bundler.

I put this in _bashrc so that whatever is in the plugins directory gets loaded.

figure out this scripts path
1
2
3
4
5
6
7
8
9
10
11
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
BASEDIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"

############################
# Pull in other bash configs
############################
for f in ${BASEDIR}/plugins/*
do
  source $f
done

OS specific plugins

As an example I use this in a file named os_x under the plugins directory.

1
2
3
if [[ $OSTYPE = darwin* ]]; then
  #OS X specific stuff here
fi

Vim

I went with pathogen to help keep vim plugins in their own tidy directories. Drop pathogen.vim in the autoload directory and add the following to _vimrc.

1
2
3
" Setup pathogen
call pathogen#infect()
call pathogen#helptags()

The bundle directory has vim plugins I use, like NERD tree. Then i mostly use git submodules to keep them all up to date. There’s a vim cast on how to do this.

A Few issues

This still leaves us with a few issues to work out.

  1. Git doesn’t store permissions, so they can be all out of whack when we pull version of files from the repository.
  2. Remembering to update submodules, and how to do that could be an issue… for me at least.

Enter the Rakefile:

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
CWD=File.expand_path(File.dirname(__FILE__))

desc "Handling initial setup and linking"
task :init do
  puts "pulling submodules"
  system "cd #{CWD} && git submodule update --init"
  replace_files
  setperms
end

desc "Update dotfiles to most recent version"
task :update do
  puts "pulling latest from repo"
  system "cd #{CWD} && git pull origin master"
  system "cd #{CWD} && git submodule foreach git pull origin master"
  replace_files
  setperms
end

desc "Set permissions - take away perms from group and other"
task :setperms do
  setperms
end

def replace_files
  files = [ '.bashrc',
            '.bash_profile',
            '.vim',
            '.vimrc',
            '.gvimrc',
  ]
  files.each do |file|
    system "rm -rf #{ENV['HOME']}/#{file}"
  end

  link_file("#{CWD}/bash/_bashrc", "#{ENV['HOME']}/.bashrc")
  link_file("#{CWD}/bash/_bash_profile", "#{ENV['HOME']}/.bash_profile")
  link_file("#{CWD}/vim/_vimrc", "#{ENV['HOME']}/.vimrc")
  link_file("#{CWD}/vim/_gvimrc", "#{ENV['HOME']}/.gvimrc")
  link_file("#{CWD}/vim", "#{ENV['HOME']}/.vim")
end

def link_file(src, target)
  system "ln -fs #{src} #{target}"
end

def setperms
  system "chmod -R g-rwx,o-rwx #{CWD}"
end
Update to the latest version
1
cd $HOME/dotfiles && rake update

Enjoy!