-
Notifications
You must be signed in to change notification settings - Fork 13.9k
/
cli_parse.rb
166 lines (152 loc) · 5.57 KB
/
cli_parse.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# -*- coding: binary -*-
module Msf
class Post
module Windows
module CliParse
require 'rex/exceptions'
#Msf::Post::Windows::CliParse::ParseError
class ParseError < ArgumentError
#
# Create a new ParseError object. Expects a method name, an error
# message, an error code, and the command that caused the error.
#
def initialize(method, einfo='', ecode=nil, clicmd=nil)
@method = method
@info = einfo
# try to look up info if not given, but code is?
@code = ecode
@clicmd = clicmd || "Unknown shell command"
end
#
# Convert a ParseError to a string.
#
def to_s
"#{@method}: Operation failed: #{@info}:#{@code} while running #{@clicmd}"
end
# The method that failed.
attr_reader :method
# The error info that occurred, typically a windows error message.
attr_reader :info
# The error result that occurred, typically a windows error code.
attr_reader :code
# The shell command that caused the error, if known
attr_reader :clicmd
end
#
# Parses output of some windows CLI commands and returns a hash with the
# keys/vals detected. If the item has multiple values, they will all be
# returned in the val separated by commas. Keys are downcased and
# symbolized (key.downcase.to_sym)
#
# sc.exe example (somewhat contrived):
# SERVICE_NAME: dumbservice
# DISPLAY_NAME: KernelSmith Dumb Service - User-mode
# TYPE : 20 WIN32_SHARE_PROCESS
# STATE : 4 RUNNING
# (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
# START_TYPE : 2 AUTO_START
# BINARY_PATH_NAME : C:\Windows\system32\svchost.exe -k LocalSystemNetworkRestricted
# DEPENDENCIES : PlugPlay
# : DumberService
# SERVICE_START_NAME : LocalSystem
#
# returns:
# {
# :service_name => "dumbservice",
# :display_name => "KernelSmith Dumb Service - User-mod",
# :state => "4 RUNNING",
# :start_type => "2 AUTO_START",
# :binary_path_name => "C:\Windows\system32\svchost.exe -k LocalSystemNetworkRestricted",
# :dependencies => "PlugPlay,DumberService"
# <...etc...>
# }
#
def win_parse_results(str)
tip = false
hashish = {}
lastkey = nil
str.each_line do |line|
line.chomp!
line.gsub!("\t",' ') # lose any tabs
if (tip == true && line =~ /^ + :/)
# then this is probably a continuation of the previous, let's append to previous
# NOTE: this will NOT pickup the (NOT_STOPPABLE, NOT_PAUSABLE), see next, but it
# will pickup when there's multiple dependencies
arr = line.scan(/\w+/)
val = arr.join(',') # join with commas, though there is probably only one item in arr
hashish[lastkey] << ",#{val}" # append to old val with preceding ','
# if that's confusing, maybe: hashish[lastkey] = "#{hashish[lastkey]},#{val}"
tip = false
elsif (tip == true && line =~ /^ + \(/)
# then this is probably a continuation of the previous, let's append to previous
# NOTE: this WILL pickup (NOT_STOPPABLE, NOT_PAUSABLE) etc
arr = line.scan(/\w+/) # put each "word" into an array
val = arr.join(',') # join back together with commas in case comma wasn't the sep
hashish[lastkey] << ",#{val}" # append to old val with preceding ','
# if that's confusing, maybe: hashish[lastkey] = "#{hashish[lastkey]},#{val}"
tip = false
elsif line =~ /^ *[A-Z]+[_]*[A-Z]+.*:/
tip = true
arr = line.split(':')
k = arr[0].strip.downcase.to_sym
# grab all remaining fields for hash val in case ':' present in val
v = arr[1..-1].join(':').strip
# now add this entry to the hash
hashish[k] = v
lastkey = k
end
end
return hashish
end
#
# Parses error output of some windows CLI commands and returns hash with
# the keys/vals detected always returns hash as follows but :errval only
# comes back from sc.exe using 'FAILED' keyword
#
# Note, most of the time the :errval will be nil, it's not usually provided
#
#
# sc.exe error example:
# [SC] EnumQueryServicesStatus:OpenService FAILED 1060:
#
# The specified service does not exist as an installed service.
# returns:
# {
# :error => "The specified service does not exist as an installed service",
# :errval => 1060
# }
#
# reg.exe error example:
# ERROR: Invalid key name.
# Type "REG QUERY /?" for usage.
# returns:
# {
# :error => "INVALID KEY NAME."
# :errval => nil
# }
#
def win_parse_error(results)
hashish = {
:error => "Unknown Error",
:errval => nil
}
# parse the results
if ma = /^error:.*/i.match(results) # if line starts with Error: just pass to regular parser
hashish.merge!(win_parse_results(ma[0].upcase)) #upcase required to satisfy regular parser
# merge results. Results from win_parse_results will override any duplicates in hashish
elsif ma = /FAILED +[0-9]+/.match(results) # look for 'FAILED ' followed by some numbers
sa = ma[0].split(' ')
hashish[:errval] = sa[1].chomp.to_i
# ^ intended to capture the numbers after the word 'FAILED' as [:errval]
ma = /^[^\[\n].+/.match(results)
hashish[:error] = ma[0].chomp.strip
# above intended to capture first non-empty line not starting with '[' or \n into [:error]
else
# do nothing, defaults are good
end
return hashish
end
end
end
end
end