rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
33.1k stars 13.76k forks source link

SQLi progress feedback #19092

Open h00die opened 1 month ago

h00die commented 1 month ago

@red0xff and I had previously discussed someway of giving feedback for SQLi where the output isn't near instant. So a blind SQLi for instance. I LOVE how sqlmap will give you _________ then fill it in like a_mini___a_or. It's a neat gui trick. Now obviously we can't do that with MSF, but when you have to dump 400+ characters and it takes 30+ minutes, its pretty boring just looking at a blank screen w/ no feedback.

I'd like to suggest a new feature of the SQLi libraries where there is a configurable percentage for feedback. So for instance, it defaults to 10 (aka every 10% completed, give feedback on progress).

Output would look similar to the command stagers:

[*] SQLi progress -  10% done (40/400 characters)
[*] SQLi progress -  20% done (80/400 characters)

Thoughts?

adfoster-r7 commented 1 month ago

It's a neat gui trick. Now obviously we can't do that with MSF

Why not? 👀

Maybe I don't understand the full context, but we definitely do that here:

https://github.com/rapid7/metasploit-framework/blob/2cf8ea39f94bbf749ca99cd6ddfc034df407d166/modules/post/multi/recon/local_exploit_suggester.rb#L145

As well as for the MSF spinner thread on console boot that spits out the intro message

[*] Starting the Metasploit Framework console...|
[*] sTarting the Metasploit Framework console...|
[*] stArting the Metasploit Framework console...|

...etc...
red0xff commented 1 month ago

I also think that displaying data before the end of the attack would have a lot of value. Had - thought about it, @adfoster-r7 the issue was that logging APIs didn't offer a portable way to move the cursor on the screen, or to print without a newline and send backspaces. The console "starting metasploit" message is displayed through $stderr.print (https://github.com/rapid7/metasploit-framework/blob/master/lib%2Fmetasploit%2Fframework%2Fcommand%2Fconsole.rb). My understanding is that it would not be portable, and that the library should only use print_XXX from the Msf::Module::UI::Message mixin, but that would be something to improve on the logging library side if that's the case.

For @h00die's suggestion, I agree that it would help, perhaps based on the size of new data that gets retrieved? (Because 10% of the data can be very insignificant, if for example, the whole data is 20 bytes, you wouldn't print 10 progress lines, or, if it's too large, printing 10% can output information that can't be easily viewed on a terminal, and 10% can take a lot of time, printing more progress lines would be better I think for this case).

nrathaus commented 1 month ago

@h00die which script are you referring to? that is not giving output

h00die commented 1 month ago

@h00die which script are you referring to? that is not giving output

└─$ grep -r "include Msf::Exploit::SQLi" *
docs/metasploit-framework.wiki/SQL-Injection-Libraries.md:include Msf::Exploit::SQLi
lib/msf/core/exploit/sqli/postgresqli/time_based_blind.rb:  include Msf::Exploit::SQLi::TimeBasedBlindMixin
lib/msf/core/exploit/sqli/postgresqli/boolean_based_blind.rb:  include Msf::Exploit::SQLi::BooleanBasedBlindMixin
lib/msf/core/exploit/sqli/mssqli/boolean_based_blind.rb:  include Msf::Exploit::SQLi::BooleanBasedBlindMixin
lib/msf/core/exploit/sqli/sqlitei/boolean_based_blind.rb:  include Msf::Exploit::SQLi::BooleanBasedBlindMixin
lib/msf/core/exploit/sqli/mysqli/boolean_based_blind.rb:  include Msf::Exploit::SQLi::BooleanBasedBlindMixin
modules/exploits/linux/http/eyesofnetwork_autodiscovery_rce.rb:  include Msf::Exploit::SQLi
modules/exploits/multi/http/cacti_pollers_sqli_rce.rb:  include Msf::Exploit::SQLi
modules/auxiliary/gather/suite_crm_export_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/gather/grandstream_ucm62xx_sql_account_guess.rb:  include Msf::Exploit::SQLi
modules/auxiliary/gather/wp_bookingpress_category_services_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/gather/peplink_bauth_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/gather/billquick_txtid_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/gather/piwigo_cve_2023_26876.rb:  include Msf::Exploit::SQLi
modules/auxiliary/sqli/dlink/dlink_central_wifimanager_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/sqli/openemr/openemr_sqli_dump.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_chopslider_id_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_loginizer_log_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_secure_copy_content_protection_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_email_sub_news_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_learnpress_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_paid_membership_pro_code_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_registrationmagic_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/wp_abandoned_cart_sqli.rb:  include Msf::Exploit::SQLi
modules/auxiliary/scanner/http/vicidial_multiple_sqli.rb:  include Msf::Exploit::SQLi

It isn't necessarily a script problem, more of a library issue.

adfoster-r7 commented 1 month ago

My understanding is that it would not be portable, and that the library should only use print_XXX from the Msf::Module::UI::Message mixin, but that would be something to improve on the logging library side if that's the case.

The print method that the local exploit suggester uses comes from rex/ui/subscriber:

>> self.methods.grep(/print/).map { |x| [x, self.method(x).source_location] }
...
 [:print, ["/tmp/metasploit-framework/lib/rex/ui/subscriber.rb", 74]],
...

https://github.com/rapid7/metasploit-framework/blob/c83a219ae40a3a56f40b3e64042963b93c0ad81c/lib/rex/ui/subscriber.rb#L71-L76

I believe in terms of portability, I've confirmed 10.times { |x| print("#{x + 1}\r"); sleep 0.1 }; print_line works on unix and windows consoles, it should work on anything that uses a terminal emulator like Metasploit Pro etc; and if there's a portability issue that comes up - it's likely something that we can likely handle similar to the color detection logic that we've got baked into framework already

nrathaus commented 1 month ago

Dev setup, use 5.0.0 openemr:

# Use admin/pass as user/password credentials to login to openemr (from OE_USER and OE_PASS below)
# MYSQL_HOST and MYSQL_ROOT_PASS are required for openemr
# MYSQL_USER, MYSQL_PASS, OE_USER, MYSQL_PASS are optional for openemr and
#   if not provided, then default to openemr, openemr, admin, and pass respectively.
version: '3.1'
services:
  mysql:
    restart: always
    image: mariadb:10.2
    command: ['mysqld','--character-set-server=utf8']
    volumes:
    - databasevolume:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
  openemr:
    restart: always
    image: openemr/openemr:5.0.0
    ports:
    - 80:80
    - 443:443
    volumes:
    - logvolume01:/var/log
    - sitevolume:/var/www/localhost/htdocs/openemr/sites
    environment:
      MYSQL_HOST: mysql
      MYSQL_ROOT_PASS: root
      MYSQL_USER: openemr
      MYSQL_PASS: openemr
      OE_USER: admin
      OE_PASS: pass
    depends_on:
    - mysql
volumes:
  logvolume01: {}
  sitevolume: {}
  databasevolume: {}

Metasploit setup:

use openemr_sqli_dump
set RHOSTS 127.0.0.1
set TARGETURI /
check

[*] Trying to detect installed version
[*] 127.0.0.1:80 - The target appears to be vulnerable.

run
nrathaus commented 1 month ago

How about we add:

print("slice: #{slice}\r")

To the truncated_query function?

Or similar?

It will display the slices pulled from the SQLi (not decode them) make it immune to non-printable characters, issues of newline embedded, etc?

nrathaus commented 1 month ago

Referring to this code:

    def truncated_query(query)
      result = [ ]
      offset = 1
      loop do
        slice = run_sql(query.sub(/\^OFFSET\^/, offset.to_s))
        offset += @truncation_length # should be same as @truncation_length for most cases
        result << slice
        print("slice: #{slice}\r")
        vprint_status "{SQLi} Truncated output: #{slice} of size #{slice.size}"
        print_warning "The block returned a string larger than the truncation size : #{slice}" if slice.length > @truncation_length
        break if slice.length < @truncation_length
      end
      result.join
    end
nrathaus commented 1 month ago

BTW: there seems to be duplicate code here, not sure why there isn't a base class that has def that are shared and overrides where necessary

nrathaus commented 1 month ago

Example of duplicate code:

def truncated_query(query)
def test_vulnerable

Actually... seems like there aren't that much duplicate code

nrathaus commented 1 month ago

I don't see how truncated_query which is where the loop happens, knows anything about the progress, it knows of the offset but not of the maximum value of this offset

nrathaus commented 1 month ago

@h00die I looked more into this, there is no way to show progress - as we don't know the amount out of a total we have collected

My only idea at the moment is to show the slice returned with a \r so that it updates, when there are no more slices it shows the outcome