@@ -340,7 +340,7 @@ function putData(url, data) {
340
340
const App = ( ) => {
341
341
let configuration = null ;
342
342
let fm = null ;
343
- let hosturl = "http://s-%1- %2-%3-%4-e.%5:%6/%7" ;
343
+ let hosturl = "http://s-%1. %2-%3-%4-e.%5:%6/%7" ;
344
344
345
345
// Push settings from configuration object (obtained from manager-config.json) to UI.
346
346
function populateManagerConfig ( ) {
@@ -367,10 +367,140 @@ const App = () => {
367
367
document . getElementById ( 'flushdns' ) . checked = configuration . getFlushDns ( ) ;
368
368
} ;
369
369
370
+
371
+ // Helper functions to allow users inputting common IP addresses instead of hexstrings, and CNAMEs
372
+
373
+ function ipToHexOrOriginal ( input ) {
374
+ let ipv4Bytes = parseIPv4 ( input ) ;
375
+ if ( ipv4Bytes !== null ) {
376
+ return bytesToHex ( ipv4Bytes ) ;
377
+ }
378
+
379
+ let ipv6Bytes = parseIPv6 ( input ) ;
380
+ if ( ipv6Bytes !== null ) {
381
+ return bytesToHex ( ipv6Bytes ) ;
382
+ }
383
+
384
+ // Not an IPv4 or IPv6, return original string, typically a CNAME record
385
+ return input ;
386
+ }
387
+
388
+ function parseIPv4 ( str ) {
389
+ const parts = str . split ( '.' ) ;
390
+ if ( parts . length !== 4 ) {
391
+ return null ;
392
+ }
393
+
394
+ let bytes = new Uint8Array ( 4 ) ;
395
+ for ( let i = 0 ; i < 4 ; i ++ ) {
396
+ const num = Number ( parts [ i ] ) ;
397
+ // Each part must be an integer within [0..255]
398
+ if (
399
+ ! Number . isInteger ( num ) ||
400
+ num < 0 ||
401
+ num > 255
402
+ ) {
403
+ return null ;
404
+ }
405
+ bytes [ i ] = num ;
406
+ }
407
+ return bytes ;
408
+ }
409
+
410
+ function parseIPv6 ( str ) {
411
+ // Split on '::' first - only one such sequence is allowed
412
+ let parts = str . split ( '::' ) ;
413
+
414
+ if ( parts . length > 2 ) {
415
+ return null ; // invalid: more than one '::'
416
+ }
417
+
418
+ let head = [ ] ;
419
+ let tail = [ ] ;
420
+
421
+ // Split the head by ':'
422
+ if ( parts [ 0 ] !== '' ) {
423
+ head = parts [ 0 ] . split ( ':' ) ;
424
+ } else {
425
+ // If parts[0] is empty, it means the address starts with '::'
426
+ head = [ ] ;
427
+ }
428
+
429
+ // Split the tail by ':'
430
+ if ( parts . length === 2 && parts [ 1 ] !== '' ) {
431
+ tail = parts [ 1 ] . split ( ':' ) ;
432
+ } else if ( parts . length === 2 && parts [ 1 ] === '' ) {
433
+ // If parts[1] is empty, it means the address ends with '::'
434
+ tail = [ ] ;
435
+ }
436
+
437
+ // Now we know the total number of blocks we have
438
+ // The full IPv6 must have 8 groups (16 bytes total)
439
+ let missingGroups = 8 - ( head . length + tail . length ) ;
440
+ if ( missingGroups < 0 ) {
441
+ // Means we have more than 8 groups in total
442
+ return null ;
443
+ }
444
+
445
+ // Build the full array of groups in hex
446
+ let groups = [ ] ;
447
+
448
+ // Validate and push the head
449
+ for ( let h of head ) {
450
+ if ( ! isValidHextet ( h ) ) {
451
+ return null ;
452
+ }
453
+ groups . push ( h ) ;
454
+ }
455
+
456
+ // Insert the zero-groups for '::'
457
+ for ( let i = 0 ; i < missingGroups ; i ++ ) {
458
+ groups . push ( '0' ) ;
459
+ }
460
+
461
+ // Validate and push the tail
462
+ for ( let t of tail ) {
463
+ if ( ! isValidHextet ( t ) ) {
464
+ return null ;
465
+ }
466
+ groups . push ( t ) ;
467
+ }
468
+
469
+ if ( groups . length !== 8 ) {
470
+ return null ; // sanity check
471
+ }
472
+
473
+ // Convert each group from hex into two bytes
474
+ let bytes = new Uint8Array ( 16 ) ;
475
+ for ( let i = 0 ; i < 8 ; i ++ ) {
476
+ let val = parseInt ( groups [ i ] , 16 ) ;
477
+ // High byte
478
+ bytes [ i * 2 ] = ( val >> 8 ) & 0xff ;
479
+ // Low byte
480
+ bytes [ i * 2 + 1 ] = val & 0xff ;
481
+ }
482
+
483
+ return bytes ;
484
+ }
485
+
486
+ function isValidHextet ( str ) {
487
+ if ( str . length === 0 || str . length > 4 ) {
488
+ return false ;
489
+ }
490
+ // Check valid hex
491
+ return / ^ [ 0 - 9 A - F a - f ] + $ / . test ( str ) ;
492
+ }
493
+
494
+ function bytesToHex ( bytes ) {
495
+ return Array . from ( bytes )
496
+ . map ( b => b . toString ( 16 ) . padStart ( 2 , '0' ) )
497
+ . join ( '' ) ;
498
+ }
499
+
370
500
function generateAttackUrl ( targetHostIPAddress , targetPort , forceDnsRebindingStrategyName ) {
371
501
return hosturl
372
- . replace ( "%1" , configuration . getAttackHostIPAddress ( ) )
373
- . replace ( "%2" , targetHostIPAddress ) // replace(/-/g, '--'))
502
+ . replace ( "%1" , ipToHexOrOriginal ( configuration . getAttackHostIPAddress ( ) ) )
503
+ . replace ( "%2" , ipToHexOrOriginal ( targetHostIPAddress ) ) // replace(/-/g, '--'))
374
504
. replace ( "%3" , Math . floor ( Math . random ( ) * 2 ** 32 ) )
375
505
. replace ( "%4" , forceDnsRebindingStrategyName === null ?
376
506
configuration . getRebindingStrategy ( ) : forceDnsRebindingStrategyName )
@@ -568,8 +698,8 @@ const App = () => {
568
698
569
699
570
700
let fid = fm . addFrame ( hosturl
571
- . replace ( "%1" , document . getElementById ( 'attackhostipaddress' ) . value )
572
- . replace ( "%2" , document . getElementById ( 'targethostipaddress' ) . value . replace ( / - / g, '--' ) )
701
+ . replace ( "%1" , ipToHexOrOriginal ( document . getElementById ( 'attackhostipaddress' ) . value ) )
702
+ . replace ( "%2" , ipToHexOrOriginal ( document . getElementById ( 'targethostipaddress' ) . value . replace ( / - / g, '--' ) ) )
573
703
. replace ( "%3" , Math . floor ( Math . random ( ) * 2 ** 32 ) )
574
704
. replace ( "%4" , document . getElementById ( 'rebindingStrategy' ) . value )
575
705
. replace ( "%5" , document . getElementById ( 'attackhostdomain' ) . value )
0 commit comments