Subversion Repositories vnag

Rev

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