Rev 10 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
8 | daniel-mar | 1 | <?php /* <ViaThinkSoftSignature> |
15 | daniel-mar | 2 | mzaKKaKFfLfRIsTQTH2cX8a9JlHCc6jY10g8Ejq7bD4x0K3yVK7cy++q5gSLwMtR9 |
3 | tdpLTt+r1f/OjfXplV0M16fpBZk8Ug2KCH0XcK1Aa4p8Ulw+jfEuw29h7oWXmOwhN |
||
4 | V6ThYMvpJzHjf5vW8e6uo+iT9lAHNmkA0eQ7oYarLO3uOUetRZBj9/njNJfhmHG1k |
||
5 | oSrJ0i7DSf9dbZQP+LOI6KGkx0sPbX424WszMbS/JZwRrEQK950h0jU9g4E73Vafi |
||
6 | bZEZT+9CDy2OhgmfpMubKFwehsNGm7ZJkN6uhH07kNqd9zgwSHnrP9g/Xdxerm+Bd |
||
7 | hF2UIt+FvnXsg1dP478YW1mN0pnvZN2DYTg1BCewgwqsJ7vTOzyxcMTKbDENfAddY |
||
8 | MeQMefZbxQs5IYajnciJXxYplVp7LiW8Nw5xsELchAZNkfOzrdFIOiCCpOKgA7NME |
||
9 | m6MBXDTQK9GxstugP0wO8e6tu7YYb8wrIOS/jtLmNsHMXwYD7sPIyUpXrs+fjtYCg |
||
10 | pKHWabY6pjEWesIYwQOSyaU7TOk0gxmNeYTw4IBTcnjkbG8tuzLTez4aJWJJUc0B5 |
||
11 | IUJrldu2ftKu8cNguaRdcZnL9IPMl1bFwXAJgKBUGUJglQMIh1zdccsy1G7hphicK |
||
12 | 6N2vWkF956yeJZYJN3qvPl+su1gSGmPxUZfJJuh1uG0WnjEAbQ5YcRKFmo+zkrsoR |
||
13 | j5NoqXH4ExP8an+Z2V6aBgWBG3HZXcI+df19GuRkJWFJ9OrCUJ5zlg6d2bNP9CE4o |
||
14 | xHnaRnoxWPGBSQZ+DScRXPcGP5IruYQohkvJGloszvid826BZkziUW22dydu+6Vw9 |
||
15 | fWNgDe90sGQvHGragPTH6oZ2RLn5YK2RPPHV8kZvrvfbELICXybwUz5x3jNEamAhD |
||
16 | 1DMrqse962LOMw3XHXog53+yH4eVOJe676M1eeAqGnx8onj3ibR1+G/1mO0xc5v8z |
||
17 | 3MQfNFPFHQDKichstUX24uMAx7rOl5MzYEC9/DtdwTibj1VtnGKr6uXYlZKB5e/hB |
||
18 | 3xqoyBGH4M7dPgJizCWRCM/P4DwIKUAjNJG7+0ZYFhehRhIHWM2Lg1ns9DkCU4YvI |
||
19 | 0DDTmTiTEWikZFRBktB3y4JbZ+lNKSQKtguyKdLgNIcbYce3qkjMLkcsxsPR0apTY |
||
20 | hNWbXbBabOTvFNkKDz2jiA1DLxyNjMUYwhQTysRBsqO+YxFMKC1nRIiLhuYkfqTnH |
||
21 | r0dbbkLNFfoekQoR/18DnH4d04wz4s3OcDr++NTO1i5B+u6Q5bl7+ur34Zi9KZOcq |
||
22 | 707rZuZ3BaDezP5G3rK1QcpMCM+jIdfxBxRfKWon3FsMqbqtD4vUM1xdK5EPRJE5S |
||
10 | daniel-mar | 23 | Q== |
8 | daniel-mar | 24 | </ViaThinkSoftSignature> */ ?> |
25 | <?php |
||
26 | |||
27 | /* |
||
28 | * VNag - Nagios Framework for PHP |
||
29 | * Developed by Daniel Marschall, ViaThinkSoft <www.viathinksoft.com> |
||
30 | * Licensed under the terms of the Apache 2.0 license |
||
31 | * |
||
15 | daniel-mar | 32 | * Revision 2020-02-14 |
8 | daniel-mar | 33 | */ |
34 | |||
35 | declare(ticks=1); |
||
36 | |||
37 | class OpenBugBountyCheck extends VNag { |
||
38 | protected $argDomain = null; |
||
15 | daniel-mar | 39 | protected $argPrivateAPI = null; |
8 | daniel-mar | 40 | |
41 | public function __construct() { |
||
42 | parent::__construct(); |
||
43 | |||
44 | $this->registerExpectedStandardArguments('Vvht'); |
||
45 | |||
46 | $this->getHelpManager()->setPluginName('check_openbugbounty'); |
||
15 | daniel-mar | 47 | $this->getHelpManager()->setVersion('1.1'); |
8 | daniel-mar | 48 | $this->getHelpManager()->setShortDescription('This plugin checks if a domain has unfixed vulnerabilities listed at OpenBugBounty.org.'); |
49 | $this->getHelpManager()->setCopyright('Copyright (C) 2011-$CURYEAR$ Daniel Marschall, ViaThinkSoft.'); |
||
15 | daniel-mar | 50 | $this->getHelpManager()->setSyntax('$SCRIPTNAME$ [-d <SingleDomain[,SingleDomain,[...]]> | -d <DomainListFile> | -p <PrivateApiUrl> ]'); |
8 | daniel-mar | 51 | $this->getHelpManager()->setFootNotes('If you encounter bugs, please contact ViaThinkSoft at www.viathinksoft.com'); |
52 | |||
53 | // Individual (non-standard) arguments: |
||
9 | daniel-mar | 54 | $this->addExpectedArgument($this->argDomain = new VNagArgument('d', 'domain', VNagArgument::VALUE_REQUIRED, 'domainOrFile', 'Domain(s) or subdomain(s), separated by comma, to be checked or a file containing domain names.')); |
15 | daniel-mar | 55 | $this->addExpectedArgument($this->argPrivateAPI = new VNagArgument('p', 'privateapi', VNagArgument::VALUE_REQUIRED, 'privateApiUrl', 'A link to your private API (https://www.openbugbounty.org/api/2/...../). Cannot be used together with argument \'-d\'.')); |
8 | daniel-mar | 56 | } |
57 | |||
58 | protected function get_cache_dir() { |
||
59 | $homedir = @getenv('HOME'); |
||
60 | if ($homedir) { |
||
61 | $try = "${homedir}/.vnag_obb_cache"; |
||
62 | if (is_dir($try)) return $try; |
||
63 | if (@mkdir($try)) return $try; |
||
64 | } |
||
65 | |||
66 | $user = posix_getpwuid(posix_geteuid()); |
||
67 | if (isset($user['dir'])) { |
||
68 | $homedir = $user['dir']; |
||
69 | $try = "${homedir}/.vnag_obb_cache"; |
||
70 | if (is_dir($try)) return $try; |
||
71 | if (@mkdir($try)) return $try; |
||
72 | } |
||
73 | |||
74 | if (isset($user['name'])) { |
||
75 | $username = $user['name']; |
||
76 | $try = "/tmp/vnag_obb_cache"; |
||
77 | if (is_dir($try)) return $try; |
||
78 | if (@mkdir($try)) return $try; |
||
79 | } |
||
80 | |||
81 | return false; // should usually never happen |
||
82 | } |
||
83 | |||
84 | function num_open_bugs($domain, $max_cache_time = 3600) { // TODO: make cache time configurable via config |
||
85 | $domain = strtolower($domain); |
||
86 | $cache_file = $this->get_cache_dir() . '/' . md5($domain); |
||
87 | |||
88 | if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) { |
||
89 | $cont = file_get_contents($cache_file); |
||
90 | } else { |
||
91 | $url = 'https://www.openbugbounty.org/api/1/search/?domain='.urlencode($domain); |
||
92 | $cont = file_get_contents($url); |
||
93 | file_put_contents($cache_file, $cont); |
||
94 | } |
||
95 | |||
96 | $fixed = 0; |
||
97 | $unfixed = 0; |
||
98 | |||
99 | $xml = simplexml_load_string($cont); |
||
100 | foreach ($xml as $x) { |
||
101 | if ($x->fixed == '1') $fixed++; |
||
102 | if ($x->fixed == '0') $unfixed++; |
||
103 | } |
||
104 | |||
105 | return array($fixed, $unfixed); |
||
106 | } |
||
107 | |||
15 | daniel-mar | 108 | function get_privateapi_data($url, $max_cache_time = 3600) { // TODO: make cache time configurable via config |
109 | $url = strtolower($url); |
||
110 | $cache_file = $this->get_cache_dir() . '/' . md5($url); |
||
111 | |||
112 | if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) { |
||
113 | $cont = file_get_contents($cache_file); |
||
114 | } else { |
||
115 | $cont = file_get_contents($url); |
||
116 | file_put_contents($cache_file, $cont); |
||
117 | } |
||
118 | |||
119 | $ary = @json_decode($cont,true); |
||
120 | if (!$ary) throw new Exception("This is probably not a correct Private API URL, or the service is down (JSON Decode failed)"); |
||
121 | return $ary; |
||
122 | } |
||
123 | |||
8 | daniel-mar | 124 | protected function cbRun($optional_args=array()) { |
125 | $domain = $this->argDomain->getValue(); |
||
15 | daniel-mar | 126 | $privateapi = $this->argPrivateAPI->getValue(); |
127 | |||
128 | if (empty($domain) && empty($privateapi)) { |
||
129 | throw new Exception("Please specify a domain or subdomain, a list of domains, or a private API Url."); |
||
8 | daniel-mar | 130 | } |
131 | |||
15 | daniel-mar | 132 | if (!empty($domain) && !empty($privateapi)) { |
133 | throw new Exception("You can either use argument '-d' or '-p', but not both."); |
||
134 | } |
||
135 | |||
136 | if (!empty($privateapi)) { |
||
137 | // Possibility 1: Private API (showing all bugs for all of your domains, with detailled information) |
||
138 | // https://www.openbugbounty.org/api/2/.../ |
||
139 | |||
140 | $sum_fixed = 0; |
||
141 | $sum_unfixed_pending = 0; |
||
142 | $sum_unfixed_disclosed = 0; |
||
143 | |||
144 | $this->setStatus(VNag::STATUS_OK); |
||
145 | |||
146 | $ary = $this->get_privateapi_data($privateapi); |
||
147 | foreach ($ary as $id => $data) { |
||
148 | /* |
||
149 | [Vulnerability Reported] => 21 September, 2017 05:13 |
||
150 | [Vulnerability Verified] => 21 September, 2017 05:14 |
||
151 | [Scheduled Public Disclosure] => 21 October, 2017 05:13 |
||
152 | [Path Status] => Patched |
||
153 | [Vulnerability Fixed] => 7 August, 2018 21:47 |
||
154 | [Report Url] => https://openbugbounty.org/reports/.../ |
||
155 | [Host] => ... |
||
156 | [Researcher] => https://openbugbounty.org/researchers/.../ |
||
157 | */ |
||
158 | |||
159 | if (empty($data['Vulnerability Reported'])) throw new Exception("This is probably not a correct Private API URL, or the service is down (Missing fields in structure)"); |
||
160 | |||
161 | $status = isset($data['Patch Status']) ? $data['Patch Status'] : $data['Path Status']; // sic! There is a typo in their API (reported, but not fixed) |
||
162 | |||
163 | if ($status == 'Patched') { |
||
164 | $sum_fixed++; |
||
165 | } else { |
||
166 | $disclosure = $data['Scheduled Public Disclosure']; |
||
167 | $time = strtotime(str_replace(',', '', $disclosure)); |
||
168 | $domain = $data['Host']; |
||
169 | $submission = $data['Report Url']; |
||
170 | if (time() > $time) { |
||
171 | $sum_unfixed_disclosed++; |
||
172 | $this->addVerboseMessage("Disclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY); |
||
173 | $this->setStatus(VNag::STATUS_CRITICAL); |
||
174 | } else { |
||
175 | $sum_unfixed_pending++; |
||
176 | $this->addVerboseMessage("Undisclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY); |
||
177 | $this->setStatus(VNag::STATUS_WARNING); |
||
178 | } |
||
179 | } |
||
180 | } |
||
181 | |||
182 | $this->setHeadline("$sum_fixed fixed, $sum_unfixed_pending unfixed (pending) and $sum_unfixed_disclosed unfixed (disclosed) issues found at your domains", true); |
||
183 | |||
184 | } else if (file_exists($domain)) { |
||
185 | // Possibility 2: File containing a list of domains |
||
8 | daniel-mar | 186 | $domains = file($domain); |
187 | $sum_fixed = 0; |
||
188 | $sum_unfixed = 0; |
||
189 | $count = 0; |
||
190 | foreach ($domains as $domain) { |
||
191 | $domain = trim($domain); |
||
192 | if ($domain == '') continue; |
||
193 | if ($domain[0] == '#') continue; |
||
194 | list($fixed, $unfixed) = $this->num_open_bugs($domain); |
||
195 | $sum_fixed += $fixed; |
||
196 | $sum_unfixed += $unfixed; |
||
197 | $count++; |
||
10 | daniel-mar | 198 | $this->addVerboseMessage("$fixed fixed and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
8 | daniel-mar | 199 | } |
200 | if ($sum_unfixed == 0) $this->setStatus(VNag::STATUS_OK); |
||
201 | if ($sum_unfixed > 0) $this->setStatus(VNag::STATUS_WARNING); // TODO: Critical, when some bugs are disclosed |
||
202 | $this->setHeadline("$sum_fixed fixed and $sum_unfixed unfixed issues found at $count domains", true); |
||
10 | daniel-mar | 203 | } else if (strpos($domain, ',') !== false) { |
15 | daniel-mar | 204 | // Possibility 3: Domains separated with comma |
9 | daniel-mar | 205 | $domains = explode(',', $domain); |
206 | $sum_fixed = 0; |
||
207 | $sum_unfixed = 0; |
||
208 | $count = 0; |
||
209 | foreach ($domains as $domain) { |
||
210 | list($fixed, $unfixed) = $this->num_open_bugs($domain); |
||
211 | $sum_fixed += $fixed; |
||
212 | $sum_unfixed += $unfixed; |
||
213 | $count++; |
||
10 | daniel-mar | 214 | $this->addVerboseMessage("$fixed fixed and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
9 | daniel-mar | 215 | } |
216 | if ($sum_unfixed == 0) $this->setStatus(VNag::STATUS_OK); |
||
217 | if ($sum_unfixed > 0) $this->setStatus(VNag::STATUS_WARNING); // TODO: Critical, when some bugs are disclosed |
||
218 | $this->setHeadline("$sum_fixed fixed and $sum_unfixed unfixed issues found at $count domains", true); |
||
8 | daniel-mar | 219 | } else { |
15 | daniel-mar | 220 | // Possibility 4: Single domain |
8 | daniel-mar | 221 | list($fixed, $unfixed) = $this->num_open_bugs($domain); |
222 | if ($unfixed == 0) $this->setStatus(VNag::STATUS_OK); |
||
223 | if ($unfixed > 0) $this->setStatus(VNag::STATUS_WARNING); // TODO: Critical, when bug is disclosed |
||
224 | $this->setHeadline("$fixed fixed and $unfixed unfixed issues found at $domain", true); |
||
225 | } |
||
226 | } |
||
227 | } |
||
9 | daniel-mar | 228 |