Skip to content

Frame Injection Vulnerability #295

@NinjaGPT

Description

@NinjaGPT

Summary

User-controlled img src allows loading untrusted frames, enabling internal service probe & info gathering, content manipulation within trusted contexts.


Details

ruoyi-admin\src\main\resources\static\ajax\libs\summernote\summernote.js

Taint Analysis

  1. The entry point is in the click event of the toolbar image button:
click: _this2.context.createInvokeHandler('imageDialog.show')
  1. This click event calls the show method of the ImageDialog class:
show() {
  var _this = this;
  this.context.invoke('editor.saveRange');
  this.showImageDialog().then(function (data) {
    _this.ui.hideDialog(_this.$dialog);
    _this.context.invoke('editor.restoreRange');
    if (typeof data === 'string') {
      if (_this.options.callbacks.onImageLinkInsert) {
        _this.context.triggerEvent('image.link.insert', data);
      } else {
        _this.context.invoke('editor.insertImage', data);
  1. The show method calls the showImageDialog method to display the image dialog and get user input:
key: "showImageDialog",
value: function showImageDialog() {
  var _this2 = this;
  return external_root_jQuery_commonjs2_jquery_commonjs_jquery_amd_jquery_default.a.Deferred(function (deferred) {
    var $imageUrl = _this2.$dialog.find('.note-image-url');
    var $imageBtn = _this2.$dialog.find('.note-image-btn');
    
    $imageBtn.click(function (event) {
      event.preventDefault();
      deferred.resolve($imageUrl.val());  // 获取用户输入的URL并通过deferred返回
  1. When the user clicks the insert button, deferred.resolve($imageUrl.val()) in showImageDialog returns the URL to the then callback of the show method, which then calls the insertImage method of the Editor class through context.invoke('editor.insertImage', data):
key: "insertImage",
value: function insertImage(src, param) {
  var _this3 = this;
  return createImage(src, param).then(function ($image) {
    _this3.beforeCommand();
    if (typeof param === 'function') {
      param($image);
    } else {
      if (typeof param === 'string') {
        $image.attr('data-filename', param);
      }
      $image.css('width', Math.min(_this3.$editable.width(), $image.width()));
    }
    $image.show();
    _this3.getLastRange().insertNode($image[0]);  // 将图片插入到编辑器中

The complete data flow is:

-> User clicks the image button in the toolbar -> triggers imageDialog.show
-> calls the ImageDialog.show() method
-> the show() method calls showImageDialog() to display the dialog
-> user enters URL in the dialog and clicks the insert button
-> deferred.resolve($imageUrl.val()) in showImageDialog() returns the URL
-> URL is passed to context.invoke('editor.insertImage', data) through the then callback of the show() method
-> finally calls the Editor.insertImage() method to insert the URL into the src attribute of the img tag


POC

Image Image

Impact

https://www.invicti.com/blog/web-security/frame-injection-attacks/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions