Mining Ruby Gem Vulnerabilities for Fun and No Profit
Larry W. Cashdollar
v0.6
Who Am I• Employed at Akamai Technologies• Hobbyist Vulnerability Researcher• 75+ CVEs• Unix Administrator • Penetration Tester Back in Late 90s• Twitter @_larry0• Enjoy Writing and Breaking Code
Agenda• Why Ruby Gems• Gem Pros/Cons• What Vulnerabilities Did I Mine• How Did I Look for Them• Automation• Crowd Sourcing• Other Areas to Mine• Improvements
The Story• Vulnerability Research Like Wading Through
Molasses*• Discover a Vulnerability Once Every Two Months?• I Wanted MOAR FASTER!• I Don’t Write Ruby
* at least for me
Why Gems
• Large Code Base• Lots of New Developers• No One Else Was Really Looking• Popularity
Pros/Cons
• Con– I don’t write Ruby… yet– True PoC Requires a Full Project
• Pro – Lots of Ruby Gem Code to Look At!– Developers New to Secure Coding Practices– Easily Download Gem Code
Ruby Gem Stats
• 3,812,982,562 Downloads• 90,120 Gems Cut Since July 2009
Simple Vulnerabilities and Easy to Find
• Insecure use of /tmp or /var/tmp• Command Injection• Clear Text User/Password Credentials• API Keys• Credentials Exposed to Process Table
The Process
10. Download A Gem20. Unpackage30. Examine40. Log50. GOTO 10
How to Look?
• Lots of Code to Dig Through• Use Tools Readily Available
– find– grep– awk– sed
Searching via Command Line
$ wget http://www.rubygems.org/gems/ftpd-1.0.1.gem$ mv ftpd-1.0.1.gem ftpd-1.0.1.tgz$ tar -zxvf ftpd-1.0.1.tgz$ cd ftpd-1.0.1$ find . -name “*.rb” –exec grep –l system {} \; > list$ for x in `cat list`; do echo “#######[$x]######”; egrep –nC3 system {} \; done > ~/results/cmd.log
Automation
• Download All the Gems!• Search Code for Possible Vulns• Sort and Categorize For Later
The Script
• Shell Script On github• Scrapes rubygems.org• Uses curl/grep/find/awk/sed • It’s an Abomination
#!/bin/sh#ruby gem scraper, grab all the gems to hAck#Larry Cashdollar, @_larry0 2/17/2015
echo "[+] Scraping rubygems.org for all $1 Gems";echo "[+] Cleaning up files";
WPATH=workdirOUTPATH=outdir
rm -rf working.$1echo "[+] Getting number of pages for letter $1";
wget https://rubygems.org/gems?letter=$1 -O $1.max
NUM=`cat $1.max | grep Next | awk -F\= '{print $36}' | awk -F\" '{print $1}'`
#will give us number of pages in Cxecho "[+] Number of pages :"$NUM
echo "[+] Downloading all $1 gems"
for x in `seq 1 $NUM`; do wget -nv https://rubygems.org/gems?letter=$1\&page=$x -O $1.$x.list ; done
echo "[+] Creating package list"
cat $1.*.list |grep "href=\"/gems/" | awk -F= '{print $3}' | sed -e 's/\/gems\///g' | sed -e 's/\"//g' | sed -e 's/>//' > main_pkg_list.$1echo "[+] Downloading all packages pages for parsing"
mkdir working.$1
for x in `cat main_pkg_list.$1`; do wget -nv https://rubygems.org/gems/$x -O working.$1/$x ; done
cd working.$1
LIST=`ls|wc -l`
echo "[+] Creating download script for $LIST gems."echo "#!/bin/sh" > download.sh.tmpfor x in `ls`; do cat $x |grep Download | awk -F\" '{print "wget -nv https://rubygems.org"$4}'; done >> download.sh.tmpcat download.sh.tmp | sort -u > download.shmkdir data.$1mv download.sh data.$1cd data.$1chmod 755 download.shecho "[+] Downloading gems.."./download.shecho "[+] Renaming files from .gem to .tar"
for x in `ls|grep gem`; do echo -n "mv $x "; echo "$x" | sed -e 's/.gem/.tar/'; done > rename
sh renamerm renameecho "[+] Unpacking"for x in `ls *.tar`; do echo $x | sed -e 's/.tar//'| xargs mkdir ; done
for x in `ls |grep -v .tar` ; do echo "- Working on $x";tar -xmf $x.tar -C $x ; done
for x in `ls|grep -v .tar`; do echo "- Unpacking $x"; tar -zxmf $x/data.tar.gz -C $x; done
echo "[+] Generating file lists of potential targets"
cd $WPATH/working.$1/data.$1echo "************************************************************"pwdecho "************************************************************"
find . -name *.rb -exec grep -l "\`#{command}\`" {} \; > cmdfile.$1.log find . -name *.rb -exec egrep -l "api_key|apikey" {} \; > api_key.$1.log find . -name *.rb -exec egrep -l "\`*\`" {} \; > backtick.$1.logfind . -name *.rb -exec egrep -l "system\(|system\s\(" {} \; > system.$1.logfind . -name *.rb -exec egrep -l '%x[\{\(\[]' {} \; > x_percent.$1.log find . -name *.rb -exec grep -l "/tmp" {} \; > tmpfile.$1.log
echo "[+] Looking for (basic) command exec vulnerabilities."#we are only finding a few of them, see http://tech.natemurray.com/2007/03/ruby-shell-commands.htmlfor x in `cat cmdfile.$1.log`; do echo "+--------------------[$x]-------------------+"; grep -nC3 "\`#{command}\`" $x; echo "+---------------------------------------------------------------------+"; done > command.$1.log.txt
for x in `cat x_percent.$1.log`; do echo "+--------------------[$x]-------------------+"; egrep -nC3 '%x[\{\(\[]' $x; echo "+---------------------------------------------------------------------+"; done > x_percent.$1.log.txt
for x in `cat backtick.$1.log`; do echo "+--------------------[$x]-------------------+"; egrep -nC3 "\`*\`" $x; echo "+---------------------------------------------------------------------+"; done > backtick.$1.log.txt
for x in `cat system.$1.log`; do echo "+--------------------[$x]-------------------+"; egrep -nC3 "system\(|system\s\(" $x; echo "+---------------------------------------------------------------------+"; done > system.$1.log.txt
echo "[+] Looking for /tmp file vulnerabilities."
for x in `cat tmpfile.$1.log`; do echo "+--------------------[$x]-------------------+"; grep -nC3 "/tmp" $x; echo "+---------------------------------------------------------------------+"; done > tmpfile.$1.log.txt
echo "[+] Looking for API key exposure vulnerabilities."
for x in `cat api_key.$1.log`; do echo "+--------------------[$x]-------------------+"; egrep -nC3 "api_key|apikey" $x; echo "+---------------------------------------------------------------------+"; done > api_key.$1.log.txt
cp command.$1.log.txt $OUTPATH/$1-command.txtcp tmpfile.$1.log.txt $OUTPATH/$1-tmpfile.txtcp x_percent.$1.log.txt $OUTPATH/$1-xexec.txtcp api_key.$1.log.txt $OUTPATH/$1-apikey.txtcp backtick.$1.log.txt $OUTPATH/$1-backtick.txtcp system.$1.log.txt $OUTPATH/$1-system.txt
cd $WPATH
echo "[+] Done"
Automation
• Pump Everything into MySQL Database• 6000 Entries• At Least 5000 obvious False Positives• FAIL
Crowd Source
• Created User Web Interface • Flag Finds as FP, Confirmed, Needs
Attention, Unknown• Invited a Few Friends
Dependencies
• https://www.ruby-toolbox.com
Going Sour
• Grave Yard of Code• < 1000 downloads• No Response from Gem Author• Pull Requests go un-Pulled• Dreaming about Ruby Code
Other Programming Languages?
• Perl Modules?• PHP Pear Packages?• Joomla! and WordPress
– Themes– Plugins
Improvements
• RFI,LFI,SQLi,XSS,CSRF?• Gem Auditing Project? • Use rubygems.org API?• Reduce FPs
– Better Regular Expressions– Parse .rb scripts, create lookup tables/symbol
tables for variables and code flow
My Github Junk• https://github.com/lcashdol/rci-info• https://github.com/lcashdol/rubygem_miner• https://github.com/lcashdol/wpthemedownloader• https://github.com/lcashdol/wpplugindownloader
WWW• http://www.vapid.dhs.org