@@ -340,7 +340,7 @@ function putData(url, data) {
340340const App = ( ) => {
341341 let configuration = null ;
342342 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" ;
344344
345345 // Push settings from configuration object (obtained from manager-config.json) to UI.
346346 function populateManagerConfig ( ) {
@@ -367,10 +367,140 @@ const App = () => {
367367 document . getElementById ( 'flushdns' ) . checked = configuration . getFlushDns ( ) ;
368368 } ;
369369
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+
370500 function generateAttackUrl ( targetHostIPAddress , targetPort , forceDnsRebindingStrategyName ) {
371501 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, '--'))
374504 . replace ( "%3" , Math . floor ( Math . random ( ) * 2 ** 32 ) )
375505 . replace ( "%4" , forceDnsRebindingStrategyName === null ?
376506 configuration . getRebindingStrategy ( ) : forceDnsRebindingStrategyName )
@@ -568,8 +698,8 @@ const App = () => {
568698
569699
570700 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, '--' ) ) )
573703 . replace ( "%3" , Math . floor ( Math . random ( ) * 2 ** 32 ) )
574704 . replace ( "%4" , document . getElementById ( 'rebindingStrategy' ) . value )
575705 . replace ( "%5" , document . getElementById ( 'attackhostdomain' ) . value )
0 commit comments