Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with sanitising XSS on Base64 encoded strings with trailing double equal (==) signs #151

Open
EricSocs opened this issue Oct 16, 2024 · 1 comment
Assignees

Comments

@EricSocs
Copy link

Stripping XSS elements from POST request containing a Base64 encoded string does not work as expected when the string ends with double equal signs.

Sometimes Base64 strings have == (double equal) signs at the end. The Anti-XSS package wrongly sanitises this, and appears to consider the first equal sign to identify a key-value pair, whereupon the remainder of the string is cut off.

The specific method which does this is AntiXSS::_remove_evil_attributes(), specifically the last regex at the bottom of the while loop (line 1456 in v4.1.42).

Use the following PHP code snippet to replicate:

// Original POST input field with 4 elements (top level delimiter "|", inner delimiter "~"), each containing a Base64 string
// The third and fourth Base64 strings have "==" at the end
$originalString = 'DataSet1~PC9TY3JlZW5pbmdQYXRoPg0KPC9TY3JlZW5pbmc+~1~1|DataSet2~GhItU2NyZWVuaW5nUGF0aD4KPC9TY3JlZW5pbmc+~1~1|DataSet3~wvU2NyZWVuaW5nUGF0aD4NCjwvUyZWVuaW5nPg==~1~1|DataSet4~N5c3RWVuaW5nUGF0aD4NCjwvU2NyZWVuaW5nPg==~1~1';

// Dynamic regex extracted at runtime; this might be slightly different depending on AntiXSS configuration
$regex = '/(?<!\p{L})(?:||style|xmlns:xdp|formaction|form|xlink:href|seekSegmentTime|FSCommand|onAbort\w*|onActivate\w*|onAttribute\w*|onAfterPrint\w*|onAfterScriptExecute\w*|onAfterUpdate\w*|onAnimationCancel\w*|onAnimationEnd\w*|onAnimationIteration\w*|onAnimationStart\w*|onAriaRequest\w*|onAutoComplete\w*|onAutoCompleteError\w*|onAuxClick\w*|onBeforeActivate\w*|onBeforeCopy\w*|onBeforeCut\w*|onBeforeInput\w*|onBeforePrint\w*|onBeforeDeactivate\w*|onBeforeEditFocus\w*|onBeforePaste\w*|onBeforePrint\w*|onBeforeScriptExecute\w*|onBeforeToggle\w*|onBeforeUnload\w*|onBeforeUpdate\w*|onBegin\w*|onBlur\w*|onBounce\w*|onCancel\w*|onCanPlay\w*|onCanPlayThrough\w*|onCellChange\w*|onChange\w*|onClick\w*|onClose\w*|onCommand\w*|onCompassNeedsCalibration\w*|onContextMenu\w*|onControlSelect\w*|onCopy\w*|onCueChange\w*|onCut\w*|onDataAvailable\w*|onDataSetChanged\w*|onDataSetComplete\w*|onDblClick\w*|onDeactivate\w*|onDeviceLight\w*|onDeviceMotion\w*|onDeviceOrientation\w*|onDeviceProximity\w*|onDrag\w*|onDragDrop\w*|onDragEnd\w*|onDragExit\w*|onDragEnter\w*|onDragLeave\w*|onDragOver\w*|onDragStart\w*|onDrop\w*|onDurationChange\w*|onEmptied\w*|onEnd\w*|onEnded\w*|onError\w*|onErrorUpdate\w*|onExit\w*|onFilterChange\w*|onFinish\w*|onFocus\w*|onFocusIn\w*|onFocusOut\w*|onFormChange\w*|onFormInput\w*|onFullScreenChange\w*|onFullScreenError\w*|onGotPointerCapture\w*|onHashChange\w*|onHelp\w*|onInput\w*|onInvalid\w*|onKeyDown\w*|onKeyPress\w*|onKeyUp\w*|onLanguageChange\w*|onLayoutComplete\w*|onLoad\w*|onLoadEnd\w*|onLoadedData\w*|onLoadedMetaData\w*|onLoadStart\w*|onLoseCapture\w*|onLostPointerCapture\w*|onMediaComplete\w*|onMediaError\w*|onMessage\w*|onMouseDown\w*|onMouseEnter\w*|onMouseLeave\w*|onMouseMove\w*|onMouseOut\w*|onMouseOver\w*|onMouseUp\w*|onMouseWheel\w*|onMove\w*|onMoveEnd\w*|onMoveStart\w*|onMozFullScreenChange\w*|onMozFullScreenError\w*|onMozPointerLockChange\w*|onMozPointerLockError\w*|onMsContentZoom\w*|onMsFullScreenChange\w*|onMsFullScreenError\w*|onMsGestureChange\w*|onMsGestureDoubleTap\w*|onMsGestureEnd\w*|onMsGestureHold\w*|onMsGestureStart\w*|onMsGestureTap\w*|onMsGotPointerCapture\w*|onMsInertiaStart\w*|onMsLostPointerCapture\w*|onMsManipulationStateChanged\w*|onMsPointerCancel\w*|onMsPointerDown\w*|onMsPointerEnter\w*|onMsPointerLeave\w*|onMsPointerMove\w*|onMsPointerOut\w*|onMsPointerOver\w*|onMsPointerUp\w*|onMsSiteModeJumpListItemRemoved\w*|onMsThumbnailClick\w*|onOffline\w*|onOnline\w*|onOutOfSync\w*|onPage\w*|onPageHide\w*|onPageShow\w*|onPaste\w*|onPause\w*|onPlay\w*|onPlaying\w*|onPointerCancel\w*|onPointerDown\w*|onPointerEnter\w*|onPointerLeave\w*|onPointerLockChange\w*|onPointerLockError\w*|onPointerMove\w*|onPointerOut\w*|onPointerOver\w*|onPointerRawUpdate\w*|onPointerUp\w*|onPopState\w*|onProgress\w*|onPropertyChange\w*|onqt_error\w*|onRateChange\w*|onReadyStateChange\w*|onReceived\w*|onRepeat\w*|onReset\w*|onResize\w*|onResizeEnd\w*|onResizeStart\w*|onResume\w*|onReverse\w*|onRowDelete\w*|onRowEnter\w*|onRowExit\w*|onRowInserted\w*|onRowsDelete\w*|onRowsEnter\w*|onRowsExit\w*|onRowsInserted\w*|onScroll\w*|onSearch\w*|onSeek\w*|onSeeked\w*|onSeeking\w*|onSelect\w*|onSelectionChange\w*|onSelectStart\w*|onStalled\w*|onStorage\w*|onStorageCommit\w*|onStart\w*|onStop\w*|onShow\w*|onSyncRestored\w*|onSubmit\w*|onSuspend\w*|onSynchRestored\w*|onTimeError\w*|onTimeUpdate\w*|onTimer\w*|onTrackChange\w*|onTransitionEnd\w*|onTransitionRun\w*|onTransitionStart\w*|onToggle\w*|onTouchCancel\w*|onTouchEnd\w*|onTouchLeave\w*|onTouchMove\w*|onTouchStart\w*|onTransitionCancel\w*|onTransitionEnd\w*|onUnload\w*|onUnhandledRejection\w*|onURLFlip\w*|onUserProximity\w*|onVolumeChange\w*|onWaiting\w*|onWebKitAnimationEnd\w*|onWebKitAnimationIteration\w*|onWebKitAnimationStart\w*|onWebKitFullScreenChange\w*|onWebKitFullScreenError\w*|onWebKitTransitionEnd\w*|onWheel)\s*=\s*(?:[^\s>]*)(.*?)/ius';
$replacement = '';

// Exact code used in AntiXSS::_remove_evil_attributes()
$strTmp = preg_replace(
    $regex,
    '$1$2' . $replacement . '$3',
    $originalString,
    -1,
    $temp_count
);

echo "$originalString\n";
echo "$strTmp\n";

echo strlen($strTmp) === strlen($originalString) ? 'PASS' : 'FAIL';
@voku voku self-assigned this Oct 16, 2024
@voku
Copy link
Owner

voku commented Oct 16, 2024

idea: (?<![\p{L}=]) instead of (?<!\p{L}) at the beginning, I need to run the tests with this change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants