├── .github
└── FUNDING.yml
├── AmazonBucket.php
├── BucketBruteForcer.php
├── DigitaloceanBucket.php
├── GoogleBucket.php
├── LICENSE.md
├── README.md
├── Utils.php
├── VERSION.md
├── preview.png
├── s3-buckets-bruteforcer.php
└── test
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [gwen001]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/AmazonBucket.php:
--------------------------------------------------------------------------------
1 | ssl = $https;
30 |
31 | $url = ($https ? 'https' : 'http') . '://';
32 | $url .= str_replace( '__BUCKET-NAME__', $this->name, self::BASE_URL );
33 | if( strlen($this->region) ) {
34 | $url = str_replace( 's3.amazonaws.com', 's3-'.$this->region.'.amazonaws.com', $url );
35 | }
36 | //var_dump($url);
37 |
38 | return $url;
39 | }
40 |
41 |
42 | public function getName() {
43 | return $this->name;
44 | }
45 | public function setName( $v ) {
46 | $this->name = trim( $v );
47 | $this->url = str_replace( '__BUCKET-NAME__', $this->name, $this->url );
48 | $this->_url = $this->url;
49 | return true;
50 | }
51 |
52 |
53 | public function getRegion() {
54 | return $this->region;
55 | }
56 | public function setRegion( $v ) {
57 | $r = trim( $v );
58 | if( strlen($r) && !in_array($r,self::T_REGION) ) {
59 | return false;
60 | }
61 | $this->region = $r;
62 | return true;
63 | }
64 |
65 |
66 | public function detectRegion()
67 | {
68 | foreach( self::T_REGION as $region )
69 | {
70 | $this->setRegion( $region );
71 | $this->canListHTTP( true, $r );
72 |
73 | if( stristr($r,'PermanentRedirect
') && stristr($r,'The bucket you are attempting to access must be addressed using the specified endpoint') ) {
74 | $m = preg_match( '#.*s3(.*).amazonaws.com#', $r, $matches );
75 | //var_dump( $matches );
76 | $region = trim( $matches[1], '-.' );
77 | }
78 |
79 | //var_dump( $region );
80 | return $region;
81 | }
82 |
83 | return false;
84 | }
85 |
86 |
87 | public function exist( &$http_code=0, $redo=false )
88 | {
89 | if( is_null($this->exist) || $redo )
90 | {
91 | $c = curl_init();
92 | curl_setopt( $c, CURLOPT_URL, $this->getUrl() );
93 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
94 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
95 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
96 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
97 | curl_setopt( $c, CURLOPT_NOBODY, true );
98 | //curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
99 | //curl_setopt( $c, CURLOPT_HEADER, true );
100 | $r = curl_exec( $c );
101 | //var_dump( $r );
102 | $t_info = curl_getinfo( $c );
103 | //var_dump( $t_info );
104 | curl_close( $c );
105 |
106 | $http_code = $t_info['http_code'];
107 |
108 | if( $http_code == 0 )
109 | {
110 | $c = curl_init();
111 | curl_setopt( $c, CURLOPT_URL, $this->getUrl(false) );
112 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
113 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
114 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
115 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
116 | curl_setopt( $c, CURLOPT_NOBODY, true );
117 | //curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
118 | //curl_setopt( $c, CURLOPT_HEADER, true );
119 | $r = curl_exec( $c );
120 | //var_dump( $r );
121 | $t_info = curl_getinfo( $c );
122 | //var_dump( $t_info );
123 | curl_close( $c );
124 |
125 | $http_code = $t_info['http_code'];
126 | }
127 |
128 | $this->exist = in_array( $http_code, self::VALID_HTTP_CODE );
129 | }
130 |
131 | return $this->exist;
132 | }
133 |
134 |
135 | public function canSetAcl( $redo=false )
136 | {
137 | if( is_null($this->canSetACL) || $redo )
138 | {
139 | $cmd = "aws s3api put-bucket-acl --grant-full-control 'uri=\"http://acs.amazonaws.com/groups/global/AllUsers\"' --bucket ".$this->name." ".(strlen($this->region)?'--region '.$this->region:'')." 2>&1";
140 | //echo $cmd."\n";
141 | exec( $cmd, $output );
142 | $output = strtolower( trim( implode("\n",$output) ) );
143 | //var_dump( $output );
144 |
145 | if( preg_match('#A client error|AllAccessDisabled|AllAccessDisabled|AccessDenied#i',$output) ) {
146 | $this->canSetACL = BucketBruteForcer::TEST_FAILED;
147 | }
148 | elseif( preg_match('#An error occurred|object has no attribute#i',$output) ) {
149 | $this->canSetACL = BucketBruteForcer::TEST_UNKNOW;
150 | }
151 | else {
152 | $this->canSetACL = BucketBruteForcer::TEST_SUCCESS;
153 | }
154 | }
155 |
156 | return $this->canSetACL;
157 | }
158 |
159 |
160 | public function canGetAcl( $redo=false )
161 | {
162 | if( is_null($this->canGetACL) || $redo )
163 | {
164 | $cmd = "aws s3api get-bucket-acl --bucket ".$this->name." ".(strlen($this->region)?'--region '.$this->region:'')." 2>&1";
165 | //echo $cmd."\n";
166 | exec( $cmd, $output );
167 | $output = strtolower( trim( implode("\n",$output) ) );
168 | //var_dump( $output );
169 |
170 | if( preg_match('#A client error|AllAccessDisabled|AllAccessDisabled|AccessDenied#i',$output) ) {
171 | $this->canGetACL = BucketBruteForcer::TEST_FAILED;
172 | }
173 | elseif( preg_match('#An error occurred|object has no attribute#i',$output) ) {
174 | $this->canGetACL = BucketBruteForcer::TEST_UNKNOW;
175 | }
176 | else {
177 | $this->canGetACL = BucketBruteForcer::TEST_SUCCESS;
178 | }
179 | }
180 |
181 | return $this->canGetACL;
182 | }
183 |
184 |
185 | public function canList( $redo=false )
186 | {
187 | if( is_null($this->canList) || $redo )
188 | {
189 | $cmd = "aws s3api list-objects --bucket ".$this->name." --max-item 5 ".(strlen($this->region)?'--region '.$this->region:'')." 2>&1";
190 | //echo $cmd."\n";
191 | exec( $cmd, $output );
192 | $output = strtolower( trim( implode("\n",$output) ) );
193 | //var_dump( $output );
194 |
195 | if( preg_match('#A client error|AllAccessDisabled|AllAccessDisabled|AccessDenied#i',$output) ) {
196 | $this->canList = BucketBruteForcer::TEST_FAILED;
197 | }
198 | elseif( preg_match('#An error occurred|object has no attribute#i',$output) ) {
199 | $this->canList = BucketBruteForcer::TEST_UNKNOW;
200 | }
201 | else {
202 | $this->canList = BucketBruteForcer::TEST_SUCCESS;
203 | }
204 | }
205 |
206 | return $this->canList;
207 | }
208 |
209 |
210 | public function canListHTTP( $redo=false, &$r=null )
211 | {
212 | if( is_null($this->canListHTTP) || $redo )
213 | {
214 | $c = curl_init();
215 | curl_setopt( $c, CURLOPT_URL, $this->getUrl($this->ssl) );
216 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
217 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
218 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
219 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
220 | curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
221 | //curl_setopt( $c, CURLOPT_HEADER, true );
222 | $r = curl_exec( $c );
223 | //var_dump( $r );
224 | $t_info = curl_getinfo( $c );
225 | //var_dump( $t_info );
226 | curl_close( $c );
227 |
228 | $http_code = $t_info['http_code'];
229 | //var_dump($http_code);
230 |
231 | if( $http_code == 200 ) {
232 | $this->canListHTTP = BucketBruteForcer::TEST_SUCCESS;
233 | } elseif( in_array($http_code,self::VALID_HTTP_CODE) ) {
234 | $this->canListHTTP = BucketBruteForcer::TEST_FAILED;
235 | } else {
236 | $this->canListHTTP = BucketBruteForcer::TEST_UNKNOW;
237 | }
238 | }
239 |
240 | return $this->canListHTTP;
241 | }
242 |
243 |
244 | public function canWrite( $redo=false )
245 | {
246 | if( is_null($this->canWrite) || $redo )
247 | {
248 | $cmd = "aws s3 cp ".__DIR__."/test s3://".$this->name." ".(strlen($this->region)?'--region '.$this->region:'')." 2>&1";
249 | //echo $cmd."\n";
250 | exec( $cmd, $output );
251 | $output = strtolower( trim( implode("\n",$output) ) );
252 | //var_dump( $output );
253 |
254 | if( preg_match('#A client error|upload failed|AllAccessDisabled|AllAccessDisabled|AccessDenied#i',$output) ) {
255 | $this->canWrite = BucketBruteForcer::TEST_FAILED;
256 | }
257 | elseif( preg_match('#An error occurred|object has no attribute#i',$output) ) {
258 | $this->canWrite = BucketBruteForcer::TEST_UNKNOW;
259 | }
260 | else {
261 | $this->canWrite = BucketBruteForcer::TEST_SUCCESS;
262 | }
263 | }
264 |
265 | return $this->canWrite;
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/BucketBruteForcer.php:
--------------------------------------------------------------------------------
1 | force_recurse = true;
71 | }
72 |
73 |
74 | public function detectRegion() {
75 | return $this->detect_region = true;
76 | }
77 |
78 |
79 | public function disableColor() {
80 | $this->disable_color = true;
81 | }
82 |
83 |
84 | public function disableTest() {
85 | $this->disable_test = true;
86 | }
87 |
88 |
89 | public function getMaxDepth() {
90 | return $this->max_depth;
91 | }
92 | public function setMaxDepth( $v ) {
93 | $this->max_depth = (int)$v;
94 | return true;
95 | }
96 |
97 |
98 | public function getPrefix() {
99 | return $this->t_prefix;
100 | }
101 | public function setPrefix( $v ) {
102 | $v = trim( $v );
103 | if( is_file($v) ) {
104 | $this->t_prefix = array_merge( $this->t_prefix, file($v,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) );
105 | } else {
106 | $this->t_prefix[] = $v;
107 | }
108 | return true;
109 | }
110 |
111 |
112 | public function getSuffix() {
113 | return $this->t_suffix;
114 | }
115 | public function setSuffix( $v ) {
116 | $v = trim( $v );
117 | if( is_file($v) ) {
118 | $this->t_suffix = array_merge( $this->t_suffix, file($v,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) );
119 | } else {
120 | $this->t_suffix[] = $v;
121 | }
122 | return true;
123 | }
124 |
125 |
126 | public function getBucket() {
127 | return $this->t_bucket;
128 | }
129 | public function setBucket( $v ) {
130 | $v = trim( $v );
131 | if( is_file($v) ) {
132 | $this->t_bucket = array_merge( $this->t_bucket, file($v,FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) );
133 | } else {
134 | $this->t_bucket[] = $v;
135 | }
136 | return true;
137 | }
138 |
139 |
140 | public function getRegion() {
141 | return $this->region;
142 | }
143 | public function setRegion( $v ) {
144 | $v = trim( $v );
145 | if( !in_array($v,AmazonBucket::T_REGION) && !in_array($v,DigitaloceanBucket::T_REGION) ) {
146 | return false;
147 | }
148 | $this->region = $v;
149 | return true;
150 | }
151 |
152 |
153 | public function getProvider() {
154 | return $this->provider;
155 | }
156 | public function setProvider( $v ) {
157 | $v = ucfirst( strtolower(trim($v)) );
158 | if( !in_array($v,self::T_PROVIDER) ) {
159 | Utils::help( 'Provider not supported' );
160 | }
161 | $this->provider = $v;
162 | return true;
163 | }
164 |
165 |
166 | public function getMaxChild() {
167 | return $this->max_child;
168 | }
169 | public function setMaxChild( $v ) {
170 | $this->max_child = (int)$v;
171 | return true;
172 | }
173 |
174 |
175 | public function getTests() {
176 | return $this->tests;
177 | }
178 | public function setTests( $v ) {
179 | $v = strtolower( trim($v) );
180 | $v = preg_replace( '#[^a-z]#', '', $v );
181 | $v = preg_replace( '#[^sglw]#', '', $v );
182 | $this->tests = $v;
183 | return true;
184 | }
185 |
186 |
187 | public function getGlue() {
188 | return $this->t_glue;
189 | }
190 | public function setGlue( $v ) {
191 | $this->t_glue = str_split( trim($v), 1 );
192 | return true;
193 | }
194 |
195 |
196 | public function getPermutation() {
197 | return $this->permutation;
198 | }
199 | public function setPermutation( $v ) {
200 | $this->permutation = (int)$v;
201 | return true;
202 | }
203 |
204 |
205 | public function getVerbosity() {
206 | return $this->verbosity;
207 | }
208 | public function setVerbosity( $v ) {
209 | $this->verbosity = (int)$v;
210 | return true;
211 | }
212 |
213 |
214 | private function cleanString( $str )
215 | {
216 | $str = preg_replace( '#[^a-z0-9\.\-\_]#', '', strtolower($str) );
217 | //$str = preg_replace( '#[^a-z0-9]#', self::WORD_SEPARATOR, $str );
218 | return $str;
219 | }
220 |
221 |
222 | private function prepare4permutation( $str )
223 | {
224 | $str = preg_replace( '#[^a-z0-9]#', self::WORD_SEPARATOR, $str );
225 | return $str;
226 | }
227 |
228 |
229 | // http://stackoverflow.com/questions/16238510/pcntl-fork-results-in-defunct-parent-process
230 | // Thousand Thanks!
231 | public function signal_handler( $signal, $pid=null, $status=null )
232 | {
233 | $pid = (int)$pid;
234 |
235 | // If no pid is provided, Let's wait to figure out which child process ended
236 | if( !$pid ){
237 | $pid = pcntl_waitpid( -1, $status, WNOHANG );
238 | }
239 |
240 | // Get all exited children
241 | while( $pid > 0 )
242 | {
243 | if( $pid && isset($this->t_process[$pid]) ) {
244 | // I don't care about exit status right now.
245 | // $exitCode = pcntl_wexitstatus($status);
246 | // if($exitCode != 0){
247 | // echo "$pid exited with status ".$exitCode."\n";
248 | // }
249 | // Process is finished, so remove it from the list.
250 | $this->n_child--;
251 | unset( $this->t_process[$pid] );
252 | }
253 | elseif( $pid ) {
254 | // Job finished before the parent process could record it as launched.
255 | // Store it to handle when the parent process is ready
256 | $this->t_signal_queue[$pid] = $status;
257 | }
258 |
259 | $pid = pcntl_waitpid( -1, $status, WNOHANG );
260 | }
261 |
262 | return true;
263 | }
264 |
265 |
266 | private function init()
267 | {
268 | $this->bucket_class = $this->provider.'Bucket';
269 |
270 | if( !strcasecmp($this->provider,'digitalocean') ) {
271 | $this->detect_region = true;
272 | }
273 |
274 | $this->t_prefix = array_map( array($this,'cleanString'), $this->t_prefix );
275 | $this->t_suffix = array_map( array($this,'cleanString'), $this->t_suffix );
276 | $this->t_bucket = array_map( array($this,'cleanString'), $this->t_bucket );
277 |
278 | if( $this->permutation >= 1 )
279 | {
280 | if( count($this->prefix) && count($this->suffix) ) {
281 | $tmp = array_merge( $this->t_prefix, $this->t_suffix );
282 | $this->t_prefix = $tmp;
283 | $this->t_suffix = $tmp;
284 | }
285 |
286 | if( $this->permutation >= 2 )
287 | {
288 | $this->t_bucket = $this->createElementPermutations( $this->t_bucket );
289 |
290 | if( $this->permutation >= 3 )
291 | {
292 | $this->t_prefix = $this->createElementPermutations( $this->t_prefix );
293 | $this->t_suffix = $this->createElementPermutations( $this->t_suffix );
294 | }
295 | }
296 | }
297 |
298 | $this->t_prefix = array_unique( $this->t_prefix );
299 | $this->t_prefix[] = '';
300 | //sort( $this->t_prefix );
301 |
302 | $this->t_suffix = array_unique( $this->t_suffix );
303 | $this->t_suffix[] = '';
304 | //sort( $this->t_suffix );
305 |
306 | $this->t_bucket = array_unique( $this->t_bucket );
307 | //sort( $this->t_bucket );
308 |
309 | //var_dump($this->t_prefix);
310 | //var_dump($this->t_suffix);
311 | //var_dump($this->t_bucket);
312 | }
313 |
314 |
315 | public function run()
316 | {
317 | $this->init();
318 | $this->t_bucket = $this->createGobalPermutations( $this->t_bucket, $this->t_prefix, $this->t_suffix, $this->t_glue );
319 |
320 | if( $this->disable_test ) {
321 | echo implode( "\n", $this->t_bucket )."\n";
322 | exit();
323 | }
324 |
325 | $this->n_bucket = count( $this->t_bucket );
326 | echo $this->n_bucket." buckets to test.\n\n";
327 | //var_dump($this->t_bucket);
328 | //exit();
329 |
330 | posix_setsid();
331 | declare( ticks=1 );
332 | pcntl_signal( SIGCHLD, array($this,'signal_handler') );
333 |
334 | $this->loop( $this->t_bucket );
335 | }
336 |
337 |
338 | private function loop( $t_buckets )
339 | {
340 | $n_bucket = count( $t_buckets );
341 | //var_dump($n_bucket);
342 | //echo $n_bucket." buckets to test.\n\n";
343 |
344 | for( $current=0 ; $current<$n_bucket ; )
345 | {
346 | if( $this->n_child < $this->max_child )
347 | {
348 | $pid = pcntl_fork();
349 |
350 | if( $pid == -1 ) {
351 | // fork error
352 | } elseif( $pid ) {
353 | // father
354 | $this->n_child++;
355 | $current++;
356 | $this->t_process[$pid] = uniqid();
357 | if( isset($this->t_signal_queue[$pid]) ){
358 | $this->signal_handler( SIGCHLD, $pid, $this->t_signal_queue[$pid] );
359 | unset( $this->t_signal_queue[$pid] );
360 | }
361 | } else {
362 | // child process
363 | usleep( rand($this->random_min_sleep,$this->random_max_sleep) );
364 | $this->testBucket( $t_buckets[$current] );
365 | exit( 0 );
366 | }
367 | }
368 |
369 | usleep( $this->loop_sleep );
370 | }
371 |
372 | while( $this->n_child ) {
373 | // surely leave the loop please :)
374 | sleep( 1 );
375 | }
376 | }
377 |
378 |
379 | private function createGobalPermutations( $t_bucket, $t_prefix, $t_suffix, $t_glue )
380 | {
381 | $t_variations = [];
382 |
383 | foreach( $t_bucket as $b )
384 | {
385 | foreach( $t_prefix as $p ) {
386 | foreach( $t_suffix as $s ) {
387 | $str = $b;
388 | if( $p != '' ) {
389 | $str = $p.self::WORD_SEPARATOR.$str;
390 | }
391 | if( $s != '' ) {
392 | $str = $str.self::WORD_SEPARATOR.$s;
393 | }
394 | foreach( $t_glue as $sep ) {
395 | $t_variations[] = str_replace( self::WORD_SEPARATOR, $sep, $str );
396 | }
397 | }
398 | }
399 | }
400 |
401 | $t_variations = array_unique( $t_variations );
402 | $t_variations = array_values( $t_variations );
403 | // sort( $t_variations );
404 | // var_dump($t_variations);
405 |
406 | return $t_variations;
407 | }
408 |
409 |
410 | private function createElementPermutations( $array )
411 | {
412 | $t_final_permut = [];
413 | $array = array_map( array($this,'prepare4permutation'), $array );
414 |
415 | foreach( $array as $i )
416 | {
417 | $tmp = explode( self::WORD_SEPARATOR, $i ); // ['www|domain|com']
418 | if( count($tmp) <= 1 ) {
419 | $t_final_permut[] = $i;
420 | } else {
421 | $t_permut = [];
422 | $this->getPermutations( $tmp, $t_permut );
423 | foreach( $t_permut as $p ) {
424 | $t_final_permut[] = implode( self::WORD_SEPARATOR, $p );
425 | }
426 | // add each part of the element ?
427 | //$t_final_permut = array_merge( $t_final_permut, $tmp );
428 | }
429 | }
430 |
431 | return $t_final_permut;
432 | }
433 |
434 |
435 | private function getPermutations( &$array, &$results, $start_i=0 )
436 | {
437 | if( $start_i == sizeof($array)-1 ) {
438 | array_push( $results, $array );
439 | }
440 |
441 | for( $i=$start_i; $igetPermutations( $array, $results, $start_i+1 );
449 |
450 | // restore old order
451 | $t = $array[$i];
452 | $array[$i] = $array[$start_i];
453 | $array[$start_i] = $t;
454 | }
455 | }
456 |
457 |
458 | /**
459 | * @todo only test the same separator character ??
460 | */
461 | private function recurse( $bucket_name )
462 | {
463 | /*if( $this->verbosity <= 1 ) {
464 | $this->output( 'Recursion level '.$this->current_depth."\n", 'yellow' );
465 | }*/
466 |
467 | $m = preg_match( '#[^0-9a-z]#i', $bucket_name, $matches );
468 | //var_dump( $matches );
469 | if( $m ) {
470 | $t_glue = [ $matches[0] ];
471 | } else {
472 | $t_glue = $this->t_glue;
473 | }
474 |
475 | $this->t_bucket[] = $bucket_name; // we don't want to retest this current bucket
476 | $t_new_variations = $this->createGobalPermutations( [$bucket_name], $this->t_prefix, $this->t_suffix, $t_glue );
477 | $t_new_variations = array_diff( $t_new_variations, $this->t_bucket );
478 | sort( $t_new_variations ); // needed because the line above can lost some keys
479 | //var_dump( $t_new_variations );
480 |
481 | $this->n_child = 0;
482 | // subthreads that create x subthreads that create x subthreads that create x subthreads that create x subthreads...........
483 | // do you really want that ?? hell no!
484 | $this->max_child = 1; // so childs can only create 1 child, that's it mother fucker!
485 | $this->t_process = [];
486 | $this->t_signal_queue = [];
487 |
488 | $this->loop( $t_new_variations );
489 | }
490 |
491 |
492 | private function testBucket( $bucket_name )
493 | {
494 | ob_start();
495 |
496 | $bucket = new $this->bucket_class();
497 | $bucket->setName( $bucket_name );
498 | $bucket->setRegion( $this->region );
499 |
500 | $e = $bucket->exist( $http_code );
501 | if( $e ) {
502 | echo 'Testing: ';
503 | $this->output( $bucket->getName()." FOUND! (".$http_code.")", (($http_code == 200) ? 'light_green' : 'green') );
504 | echo "\n";
505 | } else {
506 | if( $this->verbosity == 0 ) {
507 | echo 'Testing: ';
508 | $this->output( $bucket->getName()." , not found (".$http_code.")", 'light_grey' );
509 | echo "\n";
510 | }
511 | }
512 |
513 | if( $e && $this->detect_region )
514 | {
515 | $region = $bucket->detectRegion();
516 |
517 | if( $region ) {
518 | echo 'Region detected: '.$region."\n";
519 | $bucket->setRegion( $region );
520 | } else {
521 | echo "Region detected: not found!\n";
522 | $bucket->setRegion( null );
523 | }
524 | }
525 |
526 | if( $e && preg_match('#[sglw]#',$this->tests) )
527 | {
528 | echo "Testing permissions: ";
529 |
530 | if( strstr($this->tests,'s') ) {
531 | $s = $bucket->canSetAcl();
532 | $this->printTestResult( 'put ACL', $s, 'red' );
533 | if( $s == self::TEST_SUCCESS ) {
534 | echo "\n";
535 | return;
536 | }
537 | echo ', ';
538 | }
539 |
540 | if( strstr($this->tests,'g') ) {
541 | $g = $bucket->canGetAcl();
542 | $this->printTestResult( 'get ACL', $g, 'orange' );
543 | echo ', ';
544 | }
545 |
546 | if( strstr($this->tests,'l') ) {
547 | $l = $bucket->canList();
548 | $this->printTestResult( 'list', $l, 'orange' );
549 | echo ', ';
550 |
551 | $h = $bucket->canListHTTP(true); // force the request again, because it has already been used to detect the region or not
552 | $this->printTestResult( 'HTTP list', $h, 'orange' );
553 | echo ', ';
554 | }
555 |
556 | if( strstr($this->tests,'w') ) {
557 | $w = $bucket->canWrite();
558 | $this->printTestResult( 'write', $w, 'red' );
559 | echo ', ';
560 | }
561 |
562 | echo "\n";
563 | }
564 |
565 | $result = ob_get_contents();
566 | ob_end_clean();
567 |
568 | echo $result;
569 |
570 | if( ($e || $this->force_recurse) && $this->max_depth && $this->current_depth<$this->max_depth ) {
571 | $this->current_depth++;
572 | $this->recurse( $bucket->getName() );
573 | }
574 | }
575 |
576 |
577 | private function printTestResult( $test_name, $result, $color_if_success )
578 | {
579 | //var_dump( $test_name.'='.$result );
580 |
581 | if( $result == self::TEST_SUCCESS && (in_array($test_name,['put aCL|write']) || $this->verbosity <= 3) ) {
582 | $this->output( $test_name.' success', $color_if_success );
583 | } elseif( $this->verbosity <= 1 ) {
584 | if( $result == self::TEST_FAILED ) {
585 | $this->output( $test_name.' failed', 'light_grey' );
586 | } else {
587 | $this->output( $test_name.' an error occurred', 'light_cyan' );
588 | }
589 | }
590 | }
591 |
592 |
593 | private function output( $txt, $color )
594 | {
595 | if( $this->disable_color ) {
596 | echo $txt;
597 | } else {
598 | Utils::_print( $txt, $color );
599 | }
600 | }
601 | }
602 |
--------------------------------------------------------------------------------
/DigitaloceanBucket.php:
--------------------------------------------------------------------------------
1 | ssl = $https;
28 |
29 | $url = ($https ? 'https' : 'http') . '://';
30 | $url .= str_replace( '__BUCKET-NAME__', $this->name, self::BASE_URL );
31 | if( $this->region ) {
32 | $url = str_replace( '.digitaloceanspaces.com', '.'.$this->region.'.digitaloceanspaces.com', $url );
33 | }
34 |
35 | return $url;
36 | }
37 |
38 |
39 | public function getName() {
40 | return $this->name;
41 | }
42 | public function setName( $v ) {
43 | $this->name = trim( $v );
44 | return true;
45 | }
46 |
47 |
48 | public function getRegion() {
49 | return $this->region;
50 | }
51 | public function setRegion( $v ) {
52 | $r = trim( $v );
53 | if( !in_array($v,self::T_REGION) ) {
54 | return false;
55 | }
56 | $this->region = $r;
57 | return true;
58 | }
59 |
60 |
61 | public function detectRegion()
62 | {
63 | return $this->region;
64 | }
65 |
66 |
67 | public function exist( &$http_code=0, $redo=false )
68 | {
69 | foreach( self::T_REGION as $r )
70 | {
71 | $this->setRegion( $r );
72 |
73 | $e = $this->_exist( $http_code, true );
74 |
75 | if( $e ) {
76 | return true;
77 | }
78 | }
79 |
80 | return false;
81 | }
82 |
83 |
84 | public function _exist( &$http_code=0, $redo=false )
85 | {
86 | if( is_null($this->exist) || $redo )
87 | {
88 | $c = curl_init();
89 | curl_setopt( $c, CURLOPT_URL, $this->getUrl() );
90 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
91 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
92 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
93 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
94 | curl_setopt( $c, CURLOPT_NOBODY, true );
95 | //curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
96 | //curl_setopt( $c, CURLOPT_HEADER, true );
97 | $r = curl_exec( $c );
98 | //var_dump( $r );
99 | $t_info = curl_getinfo( $c );
100 | //var_dump( $t_info );
101 | curl_close( $c );
102 |
103 | $http_code = $t_info['http_code'];
104 |
105 | if( $http_code == 0 )
106 | {
107 | $c = curl_init();
108 | curl_setopt( $c, CURLOPT_URL, $this->getUrl(false) );
109 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
110 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
111 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
112 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
113 | curl_setopt( $c, CURLOPT_NOBODY, true );
114 | //curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
115 | //curl_setopt( $c, CURLOPT_HEADER, true );
116 | $r = curl_exec( $c );
117 | //var_dump( $r );
118 | $t_info = curl_getinfo( $c );
119 | //var_dump( $t_info );
120 | curl_close( $c );
121 |
122 | $http_code = $t_info['http_code'];
123 | }
124 |
125 | $this->exist = in_array( $http_code, self::VALID_HTTP_CODE );
126 | }
127 |
128 | return $this->exist;
129 | }
130 |
131 |
132 | public function canSetAcl( $redo=false )
133 | {
134 | return false;
135 |
136 | if( is_null($this->canSetACL) || $redo )
137 | {
138 | $cmd = "aws s3api put-bucket-acl --grant-full-control 'uri=\"http://acs.amazonaws.com/groups/global/AllUsers\"' --bucket ".$this->name." ".(strlen($this->region)?'--region '.$this->region:'')." 2>&1";
139 | //echo $cmd."\n";
140 | exec( $cmd, $output );
141 | $output = strtolower( trim( implode("\n",$output) ) );
142 | //var_dump( $output );
143 |
144 | if( preg_match('#A client error|AllAccessDisabled|AllAccessDisabled|AccessDenied#i',$output) ) {
145 | $this->canSetACL = BucketBruteForcer::TEST_FAILED;
146 | }
147 | elseif( preg_match('#An error occurred|object has no attribute#i',$output) ) {
148 | $this->canSetACL = BucketBruteForcer::TEST_UNKNOW;
149 | }
150 | else {
151 | $this->canSetACL = BucketBruteForcer::TEST_SUCCESS;
152 | }
153 | }
154 |
155 | return $this->canSetACL;
156 | }
157 |
158 |
159 | public function canGetAcl( $redo=false )
160 | {
161 | return false;
162 |
163 | if( is_null($this->canGetACL) || $redo )
164 | {
165 | $cmd = "aws s3api get-bucket-acl --bucket ".$this->name." ".(strlen($this->region)?'--region '.$this->region:'')." 2>&1";
166 | //echo $cmd."\n";
167 | exec( $cmd, $output );
168 | $output = strtolower( trim( implode("\n",$output) ) );
169 | //var_dump( $output );
170 |
171 | if( preg_match('#A client error|AllAccessDisabled|AllAccessDisabled|AccessDenied#i',$output) ) {
172 | $this->canGetACL = BucketBruteForcer::TEST_FAILED;
173 | }
174 | elseif( preg_match('#An error occurred|object has no attribute#i',$output) ) {
175 | $this->canGetACL = BucketBruteForcer::TEST_UNKNOW;
176 | }
177 | else {
178 | $this->canGetACL = BucketBruteForcer::TEST_SUCCESS;
179 | }
180 | }
181 |
182 | return $this->canGetACL;
183 | }
184 |
185 |
186 | public function canList( $redo=false )
187 | {
188 | return false;
189 |
190 | if( is_null($this->canList) || $redo )
191 | {
192 | $cmd = "aws s3api list-objects --bucket ".$this->name." --max-item 5 ".(strlen($this->region)?'--region '.$this->region:'')." 2>&1";
193 | //echo $cmd."\n";
194 | exec( $cmd, $output );
195 | $output = strtolower( trim( implode("\n",$output) ) );
196 | //var_dump( $output );
197 |
198 | if( preg_match('#A client error|AllAccessDisabled|AllAccessDisabled|AccessDenied#i',$output) ) {
199 | $this->canList = BucketBruteForcer::TEST_FAILED;
200 | }
201 | elseif( preg_match('#An error occurred|object has no attribute#i',$output) ) {
202 | $this->canList = BucketBruteForcer::TEST_UNKNOW;
203 | }
204 | else {
205 | $this->canList = BucketBruteForcer::TEST_SUCCESS;
206 | }
207 | }
208 |
209 | return $this->canList;
210 | }
211 |
212 |
213 | public function canListHTTP( $redo=false )
214 | {
215 | if( is_null($this->canListHTTP) || $redo )
216 | {
217 | $c = curl_init();
218 | curl_setopt( $c, CURLOPT_URL, $this->getUrl($this->ssl) );
219 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
220 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
221 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
222 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
223 | curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
224 | //curl_setopt( $c, CURLOPT_HEADER, true );
225 | $r = curl_exec( $c );
226 | //var_dump( $r );
227 | $t_info = curl_getinfo( $c );
228 | //var_dump( $t_info );
229 | curl_close( $c );
230 |
231 | $http_code = $t_info['http_code'];
232 |
233 | if( $http_code == 200 ) {
234 | $this->canListHTTP = BucketBruteForcer::TEST_SUCCESS;
235 | } elseif( in_array($http_code,self::VALID_HTTP_CODE) ) {
236 | $this->canListHTTP = BucketBruteForcer::TEST_FAILED;
237 | } else {
238 | $this->canListHTTP = BucketBruteForcer::TEST_UNKNOW;
239 | }
240 | }
241 |
242 | return $this->canListHTTP;
243 | }
244 |
245 |
246 | public function canWrite( $redo=false )
247 | {
248 | return false;
249 |
250 | if( is_null($this->canWrite) || $redo )
251 | {
252 | $cmd = "aws s3 cp ".__DIR__."/test s3://".$this->name." ".(strlen($this->region)?'--region '.$this->region:'')." 2>&1";
253 | //echo $cmd."\n";
254 | exec( $cmd, $output );
255 | $output = strtolower( trim( implode("\n",$output) ) );
256 | //var_dump( $output );
257 |
258 | if( preg_match('#A client error|upload failed|AllAccessDisabled|AllAccessDisabled|AccessDenied#i',$output) ) {
259 | $this->canWrite = BucketBruteForcer::TEST_FAILED;
260 | }
261 | elseif( preg_match('#An error occurred|object has no attribute#i',$output) ) {
262 | $this->canWrite = BucketBruteForcer::TEST_UNKNOW;
263 | }
264 | else {
265 | $this->canWrite = BucketBruteForcer::TEST_SUCCESS;
266 | }
267 | }
268 |
269 | return $this->canWrite;
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/GoogleBucket.php:
--------------------------------------------------------------------------------
1 | ssl = $https;
31 |
32 | $url = ($https ? 'https' : 'http') . '://';
33 | $url .= str_replace( '__BUCKET-NAME__', $this->name, self::BASE_URL );
34 | //if( $this->region ) {
35 | // $url = str_replace( '.digitaloceanspaces.com', '.'.$this->region.'.digitaloceanspaces.com', $url );
36 | //}
37 |
38 | return $url;
39 | }
40 |
41 |
42 | public function getName() {
43 | return $this->name;
44 | }
45 | public function setName( $v ) {
46 | $this->name = trim( $v );
47 | $this->url = str_replace( '__BUCKET-NAME__', $this->name, $this->url );
48 | $this->_url = $this->url;
49 | return true;
50 | }
51 |
52 |
53 | public function getRegion() {
54 | return $this->region;
55 | }
56 | public function setRegion( $v ) {
57 | $r = trim( $v );
58 | if( !in_array($v,self::T_REGION) ) {
59 | return false;
60 | }
61 | $this->region = $r;
62 | return true;
63 | }
64 |
65 |
66 | public function detectRegion()
67 | {
68 | /*foreach( self::T_REGION as $r )
69 | {
70 | $this->setRegion( $r );
71 |
72 | if( $this->canList(true) != 2 ) {
73 | return $r;
74 | }
75 | }*/
76 |
77 | return false;
78 | }
79 |
80 |
81 | public function exist( &$http_code=0, $redo=false )
82 | {
83 | if( is_null($this->exist) || $redo )
84 | {
85 | $c = curl_init();
86 | curl_setopt( $c, CURLOPT_URL, $this->getUrl() );
87 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
88 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
89 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
90 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
91 | curl_setopt( $c, CURLOPT_NOBODY, true );
92 | //curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
93 | //curl_setopt( $c, CURLOPT_HEADER, true );
94 | $r = curl_exec( $c );
95 | //var_dump( $r );
96 | $t_info = curl_getinfo( $c );
97 | //var_dump( $t_info );
98 | curl_close( $c );
99 |
100 | $http_code = $t_info['http_code'];
101 |
102 | if( $http_code == 0 )
103 | {
104 | $c = curl_init();
105 | curl_setopt( $c, CURLOPT_URL, $this->getUrl(false) );
106 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
107 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
108 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
109 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
110 | curl_setopt( $c, CURLOPT_NOBODY, true );
111 | //curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
112 | //curl_setopt( $c, CURLOPT_HEADER, true );
113 | $r = curl_exec( $c );
114 | //var_dump( $r );
115 | $t_info = curl_getinfo( $c );
116 | //var_dump( $t_info );
117 | curl_close( $c );
118 |
119 | $http_code = $t_info['http_code'];
120 | }
121 |
122 | $this->exist = in_array( $http_code, self::VALID_HTTP_CODE );
123 | }
124 |
125 | return $this->exist;
126 | }
127 |
128 |
129 | public function canSetAcl( $redo=false )
130 | {
131 | if( is_null($this->canSetACL) || $redo )
132 | {
133 | $cmd = "gsutil acl ch -u AllUsers:R gs://".$this->name." 2>&1";
134 | //echo $cmd."\n";
135 | exec( $cmd, $output );
136 | $output = strtolower( trim( implode("\n",$output) ) );
137 | //var_dump( $output );
138 |
139 | if( preg_match('#CommandException|AccessDeniedException#i',$output) ) {
140 | $this->canSetACL = BucketBruteForcer::TEST_FAILED;
141 | }
142 | else {
143 | $this->canSetACL = BucketBruteForcer::TEST_SUCCESS;
144 | }
145 | }
146 |
147 | return $this->canSetACL;
148 | }
149 |
150 |
151 | public function canGetAcl( $redo=false )
152 | {
153 | if( is_null($this->canGetACL) || $redo )
154 | {
155 | $cmd = "gsutil acl get gs://".$this->name." 2>&1";
156 | //echo $cmd."\n";
157 | exec( $cmd, $output );
158 | $output = strtolower( trim( implode("\n",$output) ) );
159 | //var_dump( $output );
160 |
161 | if( preg_match('#CommandException|AccessDeniedException#i',$output) ) {
162 | $this->canGetACL = BucketBruteForcer::TEST_FAILED;
163 | }
164 | else {
165 | $this->canGetACL = BucketBruteForcer::TEST_SUCCESS;
166 | }
167 | }
168 |
169 | return $this->canGetACL;
170 | }
171 |
172 |
173 | public function canList( $redo=false )
174 | {
175 | if( is_null($this->canList) || $redo )
176 | {
177 | $cmd = "gsutil ls gs://".$this->name." 2>&1";
178 | //echo $cmd."\n";
179 | exec( $cmd, $output );
180 | $output = strtolower( trim( implode("\n",$output) ) );
181 | //var_dump( $output );
182 |
183 | if( preg_match('#CommandException|AccessDeniedException#i',$output) ) {
184 | $this->canList = BucketBruteForcer::TEST_FAILED;
185 | }
186 | else {
187 | $this->canList = BucketBruteForcer::TEST_SUCCESS;
188 | }
189 | }
190 |
191 | return $this->canList;
192 | }
193 |
194 |
195 | public function canListHTTP( $redo=false )
196 | {
197 | if( is_null($this->canListHTTP) || $redo )
198 | {
199 | $c = curl_init();
200 | curl_setopt( $c, CURLOPT_URL, $this->getUrl($this->ssl) );
201 | curl_setopt( $c, CURLOPT_CONNECTTIMEOUT, BucketBruteForcer::REQUEST_TIMEOUT );
202 | //curl_setopt( $c, CURLOPT_FOLLOWLOCATION, true );
203 | curl_setopt( $c, CURLOPT_USERAGENT, BucketBruteForcer::T_USER_AGENT[rand(0,BucketBruteForcer::N_USER_AGENT)] );
204 | curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
205 | curl_setopt( $c, CURLOPT_SSL_VERIFYPEER, false );
206 | //curl_setopt( $c, CURLOPT_HEADER, true );
207 | $r = curl_exec( $c );
208 | //var_dump( $r );
209 | $t_info = curl_getinfo( $c );
210 | //var_dump( $t_info );
211 | curl_close( $c );
212 |
213 | $http_code = $t_info['http_code'];
214 |
215 | if( $http_code == 200 ) {
216 | $this->canListHTTP = BucketBruteForcer::TEST_SUCCESS;
217 | } elseif( in_array($http_code,self::VALID_HTTP_CODE) ) {
218 | $this->canListHTTP = BucketBruteForcer::TEST_FAILED;
219 | } else {
220 | $this->canListHTTP = BucketBruteForcer::TEST_UNKNOW;
221 | }
222 | }
223 |
224 | return $this->canListHTTP;
225 | }
226 |
227 |
228 | public function canWrite( $redo=false )
229 | {
230 | if( is_null($this->canWrite) || $redo )
231 | {
232 | $cmd = "gsutil cp ".__DIR__."/test gs://".$this->name." 2>&1";
233 | //echo $cmd."\n";
234 | exec( $cmd, $output );
235 | $output = strtolower( trim( implode("\n",$output) ) );
236 | //var_dump( $output );
237 |
238 | if( preg_match('#CommandException|AccessDeniedException#i',$output) ) {
239 | $this->canWrite = BucketBruteForcer::TEST_FAILED;
240 | }
241 | else {
242 | $this->canWrite = BucketBruteForcer::TEST_SUCCESS;
243 | }
244 | }
245 |
246 | return $this->canWrite;
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-2022 Gwendal Le Coguic
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | s3-buckets-finder
2 |
3 | PHP tool to brute force Amazon S3 bucket and test permissions.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 | ---
18 |
19 | ## Description
20 |
21 | This PHP tool searches for AWS S3 buckets using a given wordlist. When an existing bucket is found, the tool checks the permissions of the bucket:
22 | get ACL, put ACL, list, HTTP list, write
23 |
24 | ## Requirements
25 |
26 | **Amazon S3:**
27 | ```
28 | apt-get install awscli
29 | aws configure
30 | ```
31 | **Google Cloud:**
32 | https://cloud.google.com/storage/docs/gsutil_install
33 |
34 | ## Install
35 |
36 | ```
37 | git clone https://github.com/gwen001/s3-buckets-finder
38 | ```
39 |
40 | ## Usage
41 |
42 | ```
43 | Usage: php s3-buckets-bruteforcer.php [OPTIONS] --bucket
44 |
45 | Options:
46 | --bucket single bucket name or listing file
47 | --detect-region Amazon only, try to automatically detect the region of the bucket
48 | --force-recurse even if the bucket doesn't exist, the max-depth option will be applied (use this option at your own risk)
49 | --glue characters used as a separator when concatenate all elements, default are: none, dash, dot and underscore
50 | -h, --help print this help
51 | --list do no perform any test, simply list the generated permutations
52 | --max-depth max depth of recursion, if a bucket is found, another level will be added (permutations are applied), default=1, ex:
53 | if is found then test -xxx
54 | if -xxx is found then test -xxx-yyy
55 | --no-color disable colored output
56 | --perform tests to perform, default=esglw
57 | e: test if exist (always performed)
58 | s: set ACL
59 | g: get ACL
60 | l: list (cli and http)
61 | w: write
62 | --permut permutation can be tested, default=0
63 | 0: no permutation
64 | 1: if both provided prefix and suffix are permuted (prefix..suffix, suffix..prefix)
65 | 2: permutation applied only on the bucket name (a.b.c, b.c.a, ...)
66 | 3: each elements will be separately permuted, then glogal permutation
67 | --prefix single prefix or listing file
68 | --provider can be: amazon, google, digitalocean
69 | --region Amazon only, set the region (overwrite the option detect-region), value can be:
70 | us-east-1 us-east-2 us-west-1 us-west-2
71 | ap-south-1 ap-southeast-1 ap-southeast-2 ap-northeast-1 ap-northeast-2
72 | eu-central-1 eu-west-1 eu-west-2
73 | ca-central-1 sa-east-1
74 | --suffix single suffix or listing file
75 | --thread max threads, default=5
76 | -v,--verbosity set verbosity, default=0
77 | 0: everything
78 | 1: do not display not found
79 | 2: display only permissions success
80 | 3: display only set ACL and write permission success
81 |
82 | Examples:
83 | php s3-buckets-bruteforcer.php --bucket gwen001-test002
84 | php s3-buckets-bruteforcer.php --bucket listing.txt --no-color --verbosity 1
85 | php s3-buckets-bruteforcer.php --bucket listing1.txt --bucket listing2.txt --bucket listing3.txt --perform e --thread 10
86 | php s3-buckets-bruteforcer.php --bucket listing.txt --prefix prefix.txt --suffix suffix1.txt --suffix2.txt --perform esw --thread 10
87 | php s3-buckets-bruteforcer.php --bucket listing.txt --region us-east-2 --rlevel 3
88 | ```
89 |
90 | ---
91 |
92 |
93 |
94 | ---
95 |
96 | Feel free to [open an issue](/../../issues/) if you have any problem with the script.
97 |
98 |
--------------------------------------------------------------------------------
/Utils.php:
--------------------------------------------------------------------------------
1 | '0',
14 | 'black' => '0;30',
15 | 'red' => '0;31',
16 | 'green' => '0;32',
17 | 'orange' => '0;33',
18 | 'blue' => '0;34',
19 | 'purple' => '0;35',
20 | 'cyan' => '0;36',
21 | 'light_grey' => '0;37',
22 | 'dark_grey' => '1;30',
23 | 'light_red' => '1;31',
24 | 'light_green' => '1;32',
25 | 'yellow' => '1;33',
26 | 'light_blue' => '1;34',
27 | 'light_purple' => '1;35',
28 | 'light_cyan' => '1;36',
29 | 'white' => '1;37',
30 | );
31 |
32 |
33 | public static function help( $error='' )
34 | {
35 | if( is_file(__DIR__.'/README.md') ) {
36 | $help = file_get_contents( __DIR__.'/README.md' )."\n";
37 | preg_match_all( '#```(.*?)```#s', $help, $matches );
38 | //var_dump($matches);
39 | if( count($matches[1]) ) {
40 | echo trim($matches[1][0])."\n\n";
41 | }
42 | } else {
43 | echo "No help found!\n";
44 | }
45 |
46 | if( $error ) {
47 | echo "Error: ".$error."!\n";
48 | }
49 |
50 | exit();
51 | }
52 |
53 |
54 | public static function isIp( $str ) {
55 | return filter_var( $str, FILTER_VALIDATE_IP );
56 | }
57 |
58 |
59 | public static function isEmail( $str )
60 | {
61 | return filter_var( $str, FILTER_VALIDATE_EMAIL );
62 | }
63 |
64 |
65 | public static function _print( $str, $color )
66 | {
67 | echo "\033[".self::T_SHELL_COLORS[$color]."m".$str." \033[0m";
68 | }
69 | public static function _println( $str, $color )
70 | {
71 | self::_print( $str, $color );
72 | echo "\n";
73 | }
74 |
75 |
76 |
77 | public static function _array_search( $array, $search, $ignore_case=true )
78 | {
79 | if( $ignore_case ) {
80 | $f = 'stristr';
81 | } else {
82 | $f = 'strstr';
83 | }
84 |
85 | if( !is_array($search) ) {
86 | $search = array( $search );
87 | }
88 |
89 | foreach( $array as $k=>$v ) {
90 | foreach( $search as $str ) {
91 | if( $f($v, $str) ) {
92 | return $k;
93 | }
94 | }
95 | }
96 |
97 | return false;
98 | }
99 |
100 |
101 | public static function format_bytes( $size )
102 | {
103 | $units = array('b', 'kb', 'mb', 'gb', 'tb');
104 | for( $i=0 ; $size>=1024 && $i<4 ; $i++ ) {
105 | $size /= 1024;
106 | }
107 | return round($size, 2).$units[$i];
108 | }
109 | }
110 |
111 | ?>
112 |
--------------------------------------------------------------------------------
/VERSION.md:
--------------------------------------------------------------------------------
1 | 1.2.0
2 |
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwen001/s3-buckets-finder/59feb3c683854376218c229ecc0220827529bce7/preview.png
--------------------------------------------------------------------------------
/s3-buckets-bruteforcer.php:
--------------------------------------------------------------------------------
1 | #!/usr/bin/php
2 | $v )
42 | {
43 | switch( $k )
44 | {
45 | case 'bucket':
46 | $bruteforcer->setBucket( $v );
47 | break;
48 |
49 | case 'detect-region':
50 | $bruteforcer->detectRegion();
51 | break;
52 |
53 | case 'force-recurse':
54 | $bruteforcer->forceRecurse();
55 | break;
56 |
57 | case 'glue':
58 | $bruteforcer->setGlue( $v );
59 | break;
60 |
61 | case '-h':
62 | case 'help':
63 | Utils::help();
64 | break;
65 |
66 | case 'list':
67 | $bruteforcer->disableTest();
68 | break;
69 |
70 | case 'max-depth':
71 | $bruteforcer->setMaxDepth( $v );
72 | break;
73 |
74 | case 'no-color':
75 | $bruteforcer->disableColor();
76 | break;
77 |
78 | case 'perform':
79 | $bruteforcer->setTests( $v );
80 | break;
81 |
82 | case 'permut':
83 | $bruteforcer->setPermutation( $v );
84 | break;
85 |
86 | case 'prefix':
87 | $bruteforcer->setPrefix( $v );
88 | break;
89 |
90 | case 'provider':
91 | $bruteforcer->setProvider( $v );
92 | break;
93 |
94 | case 'region':
95 | if( !$bruteforcer->setRegion($v) ) {
96 | Utils::help( 'Invalid region "'.$v.'" ' );
97 | }
98 | break;
99 |
100 | case 'suffix':
101 | $bruteforcer->setSuffix( $v );
102 | break;
103 |
104 | case 'thread':
105 | $bruteforcer->setMaxChild( $v );
106 | break;
107 |
108 | case '-v':
109 | case 'verbosity':
110 | $bruteforcer->setVerbosity( (int)$v );
111 | break;
112 |
113 | default:
114 | Utils::help( 'Unknown option: '.$k );
115 | }
116 | }
117 |
118 | if( !$bruteforcer->getBucket() ) {
119 | Utils::help( 'Bucket not found' );
120 | }
121 | }
122 | // ---
123 |
124 |
125 | // main loop
126 | {
127 | $bruteforcer->run();
128 | }
129 | // ---
130 |
131 |
132 | exit();
133 |
--------------------------------------------------------------------------------
/test:
--------------------------------------------------------------------------------
1 | test
2 |
--------------------------------------------------------------------------------