Re-upped. - ssh_honeypot - a custom version of kippo for ssh honeypot analysis … | |
Log | |
Files | |
Refs | |
README | |
--- | |
commit 24dd11b49f08fa15dbc3626d3a74fefd771211b7 | |
Author: Jay Scott <[email protected]> | |
Date: Mon, 18 Aug 2014 20:06:55 +0100 | |
Re-upped. | |
Diffstat: | |
A README | 38 +++++++++++++++++++++++++++++… | |
A blacklist.rb | 53 ++++++++++++++++++++++++++++++ | |
A current-charts.rb | 102 +++++++++++++++++++++++++++++… | |
A dump.php | 40 +++++++++++++++++++++++++++++… | |
A report.php | 253 +++++++++++++++++++++++++++++… | |
A snippets.php | 79 +++++++++++++++++++++++++++++… | |
6 files changed, 565 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/README b/README | |
@@ -0,0 +1,38 @@ | |
+ | |
+This is the source code I used for my honeypot project. A lot of it is hacked | |
+together but hopefully someone will find it useful! | |
+ | |
+To ge the Ruby scripts to work you will need to install mysql and gruff gems | |
+ | |
+gem install mysql | |
+gem install gruff | |
+ | |
+The script to report an IP address uses PEAR mail but this is actually not | |
+needed, I only used it so I could send mail via gmail. You can simply change | |
+this to PHP's mail() function. | |
+ | |
+# blacklist.rb | |
+ | |
+Generates list of IP's that have been carrying out SSH attacks useful for | |
+creating blacklists for IPTables, IOS etc | |
+ | |
+# report.php | |
+ | |
+Gets the IP address from the kippo DB and reports the IP for abuse if certain | |
+conditions are met. Then saves the information to a 'report' table for | |
+displaying the information at a later date. | |
+ | |
+# dump.php | |
+ | |
+This was used to output the UML blobs in the Kippo DB to a file to be read by | |
+ajaxterm. | |
+ | |
+ | |
+# current-charts.rb | |
+ | |
+Ruby script I used to create the graphs for the front page. | |
+ | |
+# snippets.php | |
+ | |
+This is just wee snippets of code I used through-out my code that I think might | |
+be useful to some people :-) | |
diff --git a/blacklist.rb b/blacklist.rb | |
@@ -0,0 +1,53 @@ | |
+#!/usr/bin/ruby | |
+ | |
+ | |
+# Part of my honeypot.jayscott.co.uk project. | |
+# Jay Scott <[email protected]> | |
+ | |
+ | |
+require 'rubygems' | |
+require 'mysql' | |
+ | |
+# Set the dates we want to start at | |
+date = Time.new | |
+ | |
+# Change pass to your password. | |
+con_kippo = Mysql.new('localhost', 'kippo', 'pass', 'kippo') | |
+ | |
+rs_list = con_kippo.query("SELECT ip | |
+ FROM sessions | |
+ WHERE starttime LIKE '2011-#{date.month}%' | |
+ GROUP BY ip | |
+ ORDER BY ip") | |
+ | |
+ip_list = Array.new | |
+ | |
+while row = rs_list.fetch_row do | |
+ ip_list.push row[0] | |
+end | |
+ | |
+rs_list.free | |
+ | |
+# You may want to define the absolute path in the following code blocks. | |
+File.open('ip-list.txt', 'w') do |f2| | |
+ ip_list.each do|ip| | |
+ f2.puts ip | |
+ end | |
+end | |
+ | |
+File.open('ip-list-iptables.txt', 'w') do |f2| | |
+ ip_list.each do|ip| | |
+ f2.puts "iptables -A INPUT -s #{ip} -j LOG --log-prefix \"Blocked: JayScot… | |
+ f2.puts "iptables -A INPUT -s #{ip} -j DROP" | |
+ end | |
+end | |
+ | |
+File.open('ip-list-cisco.txt', 'w') do |f2| | |
+ ip_list.each do|ip| | |
+ f2.puts "access-list 1 deny host #{ip}" | |
+ end | |
+ f2.puts "access-list 1 permit any" | |
+end | |
+ | |
+ | |
+con_kippo.close | |
diff --git a/current-charts.rb b/current-charts.rb | |
@@ -0,0 +1,102 @@ | |
+#!/usr/bin/ruby | |
+# | |
+# Part of my honeypot.jayscott.co.uk project. | |
+# Jay Scott <[email protected]> | |
+# | |
+# Generates the current graph. | |
+# | |
+# gem install gruff | |
+# gem install mysql | |
+# | |
+ | |
+require 'rubygems' | |
+require 'gruff' | |
+require 'mysql' | |
+ | |
+# Set the dates we want | |
+date = Time.new | |
+current_date = "#{date.year}-0#{date.month}" | |
+string_month = Date::MONTHNAMES[date.month] | |
+ | |
+puts current_date | |
+g = Gruff::Line.new("600x300") | |
+g.title = "#{string_month} 2012 Attacks" | |
+ | |
+# Set the font options | |
+g.font = 'LiberationMono-Regular.ttf' | |
+g.marker_font_size = 12 | |
+g.legend_font_size = 12 | |
+g.title_font_size = 12 | |
+ | |
+# Set the chart colours | |
+@green = '#339933' | |
+@purple = '#cc99cc' | |
+@blue = '#336699' | |
+@yellow = '#a21764' | |
+@red = '#ff0000' | |
+@orange = '#cf5910' | |
+@black = 'black' | |
+@colors = [@yellow, @blue, @green, @red, @black, @purple, @orange] | |
+ | |
+# Set the chart look | |
+g.legend_box_size = 12 | |
+g.marker_count = 12 | |
+g.line_width = 1 | |
+g.dot_radius = 2 | |
+g.theme = { | |
+ :colors => @colors, | |
+ :marker_color => '#aea9a9', | |
+ :font_color => 'black', | |
+ :background_colors => 'white' | |
+} | |
+ | |
+# Change the password to the kippo DB | |
+con_kippo = Mysql.new('localhost', 'kippo', 'your-pass', 'kippo') | |
+ | |
+rs_sensors = con_kippo.query("SELECT id, ip FROM sensors") | |
+ | |
+while row_sensor = rs_sensors.fetch_row do | |
+ | |
+ rs_current = con_kippo.query("SELECT DISTINCT DATE(starttime) AS Date, COUNT… | |
+ FROM sessions | |
+ WHERE starttime | |
+ LIKE '#{current_date}%' | |
+ AND sensor=#{row_sensor[0]} | |
+ GROUP BY Date | |
+ ORDER BY Date") | |
+ | |
+ attack_list = [] | |
+ total_attacks = 0 | |
+ | |
+ puts "Current sensor is #{row_sensor[1]}" | |
+ puts "Number of rows #{rs_current.num_rows}" | |
+ while row = rs_current.fetch_row do | |
+ #puts "Row 1 = #{row[1]} Row 2 = #{row[0]}" | |
+ attack_list << row[1].to_i | |
+ total_attacks = total_attacks + row[1].to_i | |
+ end | |
+ | |
+ #puts "Attack list is #{attack_list}" | |
+ | |
+ if total_attacks > 0 then | |
+ legend = "#{row_sensor[1]} (#{total_attacks})" | |
+ g.data(legend, attack_list) | |
+ end | |
+ | |
+ rs_current.free | |
+ | |
+end | |
+ | |
+x = 0 | |
+days_list = {} | |
+ | |
+while x < 31 do | |
+ days_list[x] = "#{x +1}" | |
+ x = x + 1 | |
+end | |
+ | |
+g.labels = days_list | |
+g.write('current-month.png') | |
+ | |
+con_kippo.close | |
+ | |
diff --git a/dump.php b/dump.php | |
@@ -0,0 +1,40 @@ | |
+<?php | |
+ | |
+/* | |
+ * Part of my honeypot.jayscott.co.uk project. | |
+ * Jay Scott <[email protected]> | |
+ * | |
+ * Script that I ran on the cron to dump the .log files into a directory for | |
+ * ajax-term to read. | |
+ * | |
+ */ | |
+ | |
+# Change to your information. | |
+$db = mysql_pconnect("localhost","kippo","your-password"); | |
+mysql_select_db("kippo",$db); | |
+ | |
+# I found that if the log was < 85 there was normally no command issued. | |
+$QUERY_TTY = mysql_query("SELECT id, session FROM ttylog WHERE LENGTH(ttylog) … | |
+ | |
+if($QUERY_TTY) | |
+ echo "Query Complete\n"; | |
+else | |
+ echo "Query Failed\n"; | |
+ | |
+$num_rows = mysql_num_rows($QUERY_TTY); | |
+ | |
+echo "Rows = $num_rows \n"; | |
+echo mysql_error(); | |
+ | |
+# Change location-to-store-logs to where you want to store the Kippo log files… | |
+while($tty_row = mysql_fetch_array($QUERY_TTY)) { | |
+ mysql_query("SELECT ttylog FROM ttylog WHERE id=" . $tty_row['id'] . " into … | |
+ if($tty_row){ | |
+ echo " Command is successful \n"; | |
+ echo "ttylog = " . $tty_row['id'] . "\n"; | |
+ } | |
+ else | |
+ echo " Command not successful \n"; | |
+} | |
+ | |
+?> | |
diff --git a/report.php b/report.php | |
@@ -0,0 +1,253 @@ | |
+<?php | |
+ | |
+/* | |
+ * Part of my honeypot.jayscott.co.uk project. | |
+ * Jay Scott <[email protected]> | |
+ * | |
+ * Gets the IP address from the kippo DB and reports the IP for abuse if certa… | |
+ * conditions are met. Then saves the information to a 'report' table for | |
+ * displaying information at a later date. | |
+ * | |
+ * I still have debugging echo statements etc floating about :p | |
+ * | |
+ * | |
+ * report table - added to kippo database | |
+ * | |
+ * CREATE TABLE IF NOT EXISTS `report` ( | |
+ * `id` int(11) NOT NULL AUTO_INCREMENT, | |
+ * `name` char(50) NOT NULL, | |
+ * `ip` varchar(15) NOT NULL, | |
+ * `contact` varchar(200) NOT NULL, | |
+ * `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMES… | |
+ * `replied` tinyint(1) NOT NULL DEFAULT '0', | |
+ * `contacted` tinyint(1) NOT NULL DEFAULT '1', | |
+ * `notes` text NOT NULL, | |
+ * PRIMARY KEY (`id`) | |
+ * ); | |
+ * | |
+ * | |
+ * Uses pears Mail script, this can be easily change to PHP's mail(). | |
+ * | |
+ * pear install Mail | |
+ * | |
+ */ | |
+ | |
+error_reporting(0); | |
+ | |
+require_once "Mail.php"; | |
+ | |
+function attackAttempts($id, $db) | |
+{ | |
+ $result = mysql_query("SELECT COUNT(id) AS IPCOUNT FROM sessions WHERE ip ='… | |
+ if ($row = mysql_fetch_array($result)) | |
+ return (int) $row['IPCOUNT']; | |
+} | |
+ | |
+function attackSuccessful($id, $db) | |
+{ | |
+ $result = mysql_query("SELECT auth.session, auth.success FROM auth | |
+ INNER JOIN sessions ON auth.session… | |
+ WHERE auth.success=1 AND sessions.ip='$id'"); | |
+ | |
+ $num_rows = (int) mysql_num_rows($result); | |
+ return $num_rows; | |
+} | |
+ | |
+/* Change to your Kippo DB password */ | |
+$db = mysql_pconnect("localhost", "kippo", "yourpassword"); | |
+mysql_select_db("kippo", $db); | |
+ | |
+$previous_date = date("Y-m-d", strtotime("-1 day")); | |
+ | |
+$QUERY_ATTACKS = mysql_query("SELECT auth.session, auth.`timestamp`, MAX(sessi… | |
+ sessions.ip, sessions.sensor | |
+ FROM auth INNER JOIN sessions ON auth.session = … | |
+ WHERE timestamp >= '$previous_date' | |
+ GROUP BY sessions.ip | |
+ ORDER BY auth.id "); | |
+ | |
+while ($ROW_ATTACKER = mysql_fetch_array($QUERY_ATTACKS)) | |
+ { | |
+ $IPADDRESS = $ROW_ATTACKER["ip"]; | |
+ $START = $ROW_ATTACKER["MAXTIME"]; | |
+ $END = $ROW_ATTACKER["MINTIME"]; | |
+ $SENSOR = $ROW_ATTACKER["sensor"]; | |
+ $SESSION = $ROW_ATTACKER["session"]; | |
+ $TIMESTAMP = $ROW_ATTACKER["timestamp"]; | |
+ | |
+ /* Already in the DB? dont report again */ | |
+ $IP_EXISTS = mysql_query("SELECT contacted FROM report WHERE ip='$IPADDRES… | |
+ | |
+ if ($ROW_EXISTS = mysql_fetch_array($IP_EXISTS)) { | |
+ continue; | |
+ } | |
+ | |
+ echo "IP = $IPADDRESS\n"; | |
+ | |
+ $attack_success = 0; | |
+ $total_attacks = attackAttempts($IPADDRESS, $db); | |
+ $attack_success = attackSuccessful($IPADDRESS, $db); | |
+ | |
+ if ($total_attacks > 10 ) | |
+ echo "More than 10 attempts ($total_attacks) ($attack_success)\n"; | |
+ else if ($attack_success > 0) | |
+ echo "Attack Success ($total_attacks) ($attack_success)\n"; | |
+ else { | |
+ echo "Less than 10 attempts ($total_attacks) ($attack_success)\n"; | |
+ continue; | |
+ } | |
+ | |
+ $email = array(); | |
+ | |
+ unset($f); | |
+ /* Shouldn't need to sanitise the IP address */ | |
+ exec("whois $IPADDRESS ", $f); | |
+ unset($tmpname); | |
+ unset($output); | |
+ | |
+ foreach ($f as $output) { | |
+ if (stripos($output, "netname:") === 0) | |
+ $tmpname = explode(':',$output); | |
+ else if (stripos($output, "owner:") === 0) | |
+ $tmpname = explode(':',$output); | |
+ | |
+ preg_match('/[\._a-zA-Z0-9-]+@[\._a-zA-Z0-9-]+/i', $output, $matches); | |
+ | |
+ $email[] = strtolower($matches[0]); | |
+ $email = array_filter($email); | |
+ } | |
+ | |
+ $email = array_filter($email); | |
+ $email = array_unique($email); | |
+ | |
+ $EMAILS = implode(" ",$email); | |
+ $NAME = trim($tmpname[1]); | |
+ $email_parts = explode(" ", $EMAILS); | |
+ | |
+ foreach ($email_parts as $b_email) { | |
+ | |
+ $EMAIL_ABUSE = 0; | |
+ $tmp_username = substr($b_email, 0, strpos($b_email, '@')); | |
+ $tmp_username = strtolower($tmp_username); | |
+ if ( $tmp_username == "abuse" || $tmp_username == "support") { | |
+ $EMAIL_ABUSE = 1; | |
+ $EMAILS = $b_email; | |
+ } | |
+ } | |
+ | |
+ if (empty($email)) { | |
+ $INSERT_REPORT = mysql_query("INSERT INTO report (name, ip, contact, con… | |
+ continue; | |
+ } else { | |
+ $INSERT_REPORT = mysql_query("INSERT INTO report (name, ip, contact, d… | |
+ } | |
+ | |
+ unset($to); | |
+ $parts = explode(" ", $EMAILS); | |
+ if (sizeof($parts) == 1) | |
+ $to = rtrim($parts[0],'.'); | |
+ else { | |
+ foreach ($parts as $send_cc) { | |
+ $send_cc = rtrim($send_cc,'.'); | |
+ $to .= "$send_cc,"; | |
+ } | |
+ | |
+ $to = substr($to, 0, -1); | |
+ } | |
+ echo "TO = $to"; | |
+ | |
+ /* Kippo stored the IP of the sensor as a name in the 'sensors' table, get | |
+ * the sensor ID and then identify IP. | |
+ */ | |
+ switch ($SENSOR) | |
+ { | |
+ case 1: | |
+ $TARGET = "ip-removed"; | |
+ break; | |
+ case 2: | |
+ $TARGET = "ip-removed"; | |
+ break; | |
+ case 3: | |
+ $TARGET = "ip-removed"; | |
+ break; | |
+ case 4: | |
+ $TARGET = "ip-removed"; | |
+ break; | |
+ case 5: | |
+ $TARGET = "ip-removed"; | |
+ break; | |
+ case 6: | |
+ $TARGET = "ip-removed"; | |
+ break; | |
+ case 7: | |
+ $TARGET = "ip-removed"; | |
+ break; | |
+ case 8: | |
+ $TARGET = "ip-removed"; | |
+ break; | |
+ } | |
+ | |
+ /* My SMTP information, change to yours or remove and add the default | |
+ PHP mail() command */ | |
+ $host = "ssl://smtp.gmail.com"; | |
+ $port = "465"; | |
+ $username = ""; | |
+ $password = ''; | |
+ | |
+ $subject = "SSH attack from $IPADDRESS"; | |
+ $from = '[email protected]'; | |
+ $headers = "From: $from \r\n" . "Reply-To: $from \r\n"; | |
+ | |
+ $message = "To abuse/support,"; | |
+ | |
+ if ($EMAIL_ABUSE = 0) { | |
+ $message .= " | |
+ | |
+ Please note I could not find a abuse or support email address in an | |
+ WHOIS lookup."; | |
+ } | |
+ | |
+ $message .= " | |
+ | |
+ I run a honeypot network that reports any attacking IP address or | |
+ successful logins from unauthorised IP address. | |
+ | |
+ The IP $IPADDRESS first gained access or attempted to access the | |
+ honeypot on $START GMT against the IP address $TARGET."; | |
+ | |
+ $message .= " | |
+ | |
+ It maybe that $IPADDRESS has been compromised, is an active | |
+ participant in a botnet or is being used as a SSH tunnel. | |
+ | |
+ You may wish to monitor the IP Address. You can view more details about | |
+ the attack such as any more attacks carried out, amount of attacks and | |
+ even watch the attack if they successfully logged in here: | |
+ | |
+ http://honeypot.jayscott.co.uk/ip/$IPADDRESS | |
+ | |
+ If you would like any advice or require further information please | |
+ feel free to contact me, [email protected]. | |
+ | |
+ Regards, | |
+ Jay Scott"; | |
+ | |
+ $headers = array ('From' => $from, | |
+ 'To' => $to, | |
+ 'Subject' => $subject); | |
+ $smtp = Mail::factory('smtp', | |
+ array ('host' => $host, | |
+ 'port' => $port, | |
+ 'auth' => true, | |
+ 'username' => $username, | |
+ 'password' => $password)); | |
+ | |
+ $mail = $smtp->send($to, $headers, $message); | |
+ | |
+ if (PEAR::isError($mail)) { | |
+ echo(" - " . $mail->getMessage() . "\n"); | |
+ } else { | |
+ echo(" - Message sent\n\n"); | |
+ } | |
+ } | |
+?> | |
diff --git a/snippets.php b/snippets.php | |
@@ -0,0 +1,79 @@ | |
+<? | |
+ | |
+/* | |
+ * Part of my honeypot.jayscott.co.uk project. | |
+ * Jay Scott <[email protected]> | |
+ * | |
+ * Various code snippets I used in pages through-out the project, didn't think | |
+ * there was much point in displaying all of the HTML etc. | |
+ */ | |
+ | |
+ | |
+/* === Get unique Malware links === */ | |
+ | |
+$QUERY_DOWNLOAD = mysql_query("SELECT input.input, input.timestamp, sessions.i… | |
+ FROM input INNER JOIN sessions | |
+ ON input.session = sessions.id | |
+ WHERE input.input LIKE '%wget%' | |
+ GROUP BY input.input | |
+ ORDER BY input.timestamp DESC "); | |
+ | |
+while ($DOWNLOAD_ROW = mysql_fetch_array($QUERY_DOWNLOAD)) { | |
+ if (strlen($DOWNLOAD_ROW['input']) > 8) { | |
+ | |
+ $Date = strtotime($DOWNLOAD_ROW["timestamp"]); | |
+ $myDate = date('D jS M, G:i:s', $Date); | |
+ | |
+ $URL = htmlspecialchars($DOWNLOAD_ROW['input']); | |
+ | |
+ echo "<tr><td>$myDate</td> | |
+ <td>" . substr($URL, 5) . "</td></tr>"; | |
+ } | |
+} | |
+ | |
+ | |
+ | |
+/* === Get unique passwords === */ | |
+ | |
+$sql_date = mysql_real_escape_string($_GET['date']); | |
+ | |
+if ($sql_date == 'all') { | |
+ $previous_date = "2011-02-01"; /* date I started logging via sql */ | |
+} else if ($sql_date == 'week') { | |
+ $previous_date = date("Y-m-d", strtotime("-7 day")); | |
+} else if ($sql_date == 'month') { | |
+ $previous_date = date("Y-m-d", strtotime("-30 day")); | |
+} else { | |
+ $previous_date = date("Y-m-d", strtotime("-1 day")); | |
+} | |
+ | |
+/* simply change password to username for username stats */ | |
+$query_passwords = mysql_query("SELECT COUNT(password) AS PCOUNT, password | |
+ FROM auth WHERE password <> '' | |
+ AND timestamp >= '$previous_date' | |
+ GROUP BY password | |
+ ORDER BY PCOUNT DESC LIMIT 20"); | |
+ | |
+ | |
+/* === Showing information on the attack === */ | |
+ | |
+$QUERY_CLIENT = mysql_query("SELECT version FROM clients | |
+ WHERE id = '$CLIENT' | |
+ LIMIT 1"); | |
+ | |
+$CLIENT_SEARCH = strtolower($ROWS_CLIENT['version']); | |
+ | |
+ if (strpos($CLIENT_SEARCH, "putty")) | |
+ echo "<b>Connected Manually</b> "; | |
+ else if (strpos($CLIENT_SEARCH, "libssh")) | |
+ echo "<b>Used a C scanner</b> "; | |
+ else if (strpos($CLIENT_SEARCH, "winscp")) | |
+ echo "<b>Used WinSCP</b> "; | |
+ else if (strpos($CLIENT_SEARCH, "openssh")) | |
+ echo "<b>Connected Manually</b> "; | |
+ else if (strpos($CLIENT_SEARCH, "nmap")) | |
+ echo "<b>NMap Scan</b> "; | |
+ | |
+ | |
+ | |
+?> |