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

UAF in Tkinter module #71441

Closed
EminGhuliev mannequin opened this issue Jun 7, 2016 · 7 comments
Closed

UAF in Tkinter module #71441

EminGhuliev mannequin opened this issue Jun 7, 2016 · 7 comments
Labels
topic-tkinter type-crash A hard crash of the interpreter, possibly with a core dump

Comments

@EminGhuliev
Copy link
Mannequin

EminGhuliev mannequin commented Jun 7, 2016

BPO 27254
Nosy @serhiy-storchaka

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2016-07-05.06:09:30.272>
created_at = <Date 2016-06-07.18:29:00.965>
labels = ['expert-tkinter', 'type-crash']
title = 'UAF in Tkinter module'
updated_at = <Date 2016-07-05.06:09:47.289>
user = 'https://bugs.python.org/EminGhuliev'

bugs.python.org fields:

activity = <Date 2016-07-05.06:09:47.289>
actor = 'Emin Ghuliev'
assignee = 'none'
closed = True
closed_date = <Date 2016-07-05.06:09:30.272>
closer = 'Emin Ghuliev'
components = ['Tkinter']
creation = <Date 2016-06-07.18:29:00.965>
creator = 'Emin Ghuliev'
dependencies = []
files = []
hgrepos = []
issue_num = 27254
keywords = []
message_count = 6.0
messages = ['267714', '267727', '267795', '267798', '267800', '267860']
nosy_count = 2.0
nosy_names = ['serhiy.storchaka', 'Emin Ghuliev']
pr_nums = []
priority = 'normal'
resolution = 'third party'
stage = None
status = 'closed'
superseder = None
type = 'crash'
url = 'https://bugs.python.org/issue27254'
versions = ['Python 2.7', 'Python 3.5', 'Python 3.6']

@EminGhuliev
Copy link
Mannequin Author

EminGhuliev mannequin commented Jun 7, 2016

/* This is used to get the application class for Tk 4.1 and up */
    argv0 = (char*)attemptckalloc(strlen(className) + 1); //<=== classname allocated
    if (!argv0) {
        PyErr_NoMemory();
        Py_DECREF(v);
        return NULL;
    }
strcpy(argv0, className); <==== //classname copy to argv0
if (Py_ISUPPER(Py_CHARMASK(argv0[0])))
    argv0[0] = Py_TOLOWER(Py_CHARMASK(argv0[0]));
Tcl_SetVar(v->interp, "argv0", argv0, TCL_GLOBAL_ONLY); // argv0 passed to v->interp and freed;
ckfree(argv0);

then v->interp passed to the Tcl_AppInit function
if (Tcl_AppInit(v->interp) != TCL_OK)

in Tcl_AppInit call to (and passed the v->interp) the Tcl_DStringAppend. allocates the specified byte Tcl_DStringAppend function then heap memory passed to memcpy.

Realloc arguments
presentation in the native tcl allocator;
char *
Tcl_Realloc(ptr, size)

disassemble:
gdb> print /x $rdi
$4 = 0x7ffff03c8810
0x7ffff03c8814: 0x41414141 .......
gdb> print /x $rsi
$2 = 0x3ffffe
0x00007ffff3a07dfe <+46>: call 0x7ffff3935040 <Tcl_Realloc>
after return to the caller function. Performed memory copy operation.

0x00007ffff3a07e0a <+58>: lea rdi,[rax+rdx*1] < === destination buffer

$rax = 0x7fffeffc5810 - $rdx = 0x100000
$rax+$rdx = 0x7ffff00c5810

0x00007ffff3a07e0e <+62>: mov rsi,r12 < === source buffer
0x00007ffff3a07e11 <+65>: movsxd rdx,ebp <=== 0xfffff
0x00007ffff3a07e14 <+68>: call 0x7ffff39155c0 <memcpy@plt>

copy to $rdi bytes to $rsi buffer with 0xfffff byte;

ASAN report.

=================================================================
==27988==ERROR: AddressSanitizer: heap-use-after-free on address 0x7f4e6ba64810 at pc 0x4665ea bp 0x7fff89a4ab80 sp 0x7fff89a4a340
READ of size 1048575 at 0x7f4e6ba64810 thread T0
==27988==WARNING: Trying to symbolize code, but external symbolizer is not initialized!
#0 0x4665e9 (/home/eminus/Downloads/Python-2.7.11/python+0x4665e9)
#1 0x7f4e6f0a3e18 (/usr/lib/x86_64-linux-gnu/libtcl8.6.so+0x116e18)
#2 0x7f4e6f38744e (/usr/lib/x86_64-linux-gnu/libtk8.6.so+0x6244e)
#3 0x7f4e6f6b6e4c (/home/eminus/Downloads/Python-2.7.11/build/lib.linux-x86_64-2.7/_tkinter.so+0x19e4c)
#4 0x7f4e6f6a7fc5 (/home/eminus/Downloads/Python-2.7.11/build/lib.linux-x86_64-2.7/_tkinter.so+0xafc5)
#5 0x5e1813 (/home/eminus/Downloads/Python-2.7.11/python+0x5e1813)
#6 0x5d319c (/home/eminus/Downloads/Python-2.7.11/python+0x5d319c)
#7 0x721353 (/home/eminus/Downloads/Python-2.7.11/python+0x721353)
#8 0x4acb2a (/home/eminus/Downloads/Python-2.7.11/python+0x4acb2a)
#9 0x4b6c62 (/home/eminus/Downloads/Python-2.7.11/python+0x4b6c62)
#10 0x4acb2a (/home/eminus/Downloads/Python-2.7.11/python+0x4acb2a)
#11 0x5f0823 (/home/eminus/Downloads/Python-2.7.11/python+0x5f0823)
#12 0x4b0a08 (/home/eminus/Downloads/Python-2.7.11/python+0x4b0a08)
#13 0x4acb2a (/home/eminus/Downloads/Python-2.7.11/python+0x4acb2a)
#14 0x5e2d19 (/home/eminus/Downloads/Python-2.7.11/python+0x5e2d19)
#15 0x5d319c (/home/eminus/Downloads/Python-2.7.11/python+0x5d319c)
#16 0x5d2041 (/home/eminus/Downloads/Python-2.7.11/python+0x5d2041)
#17 0x660980 (/home/eminus/Downloads/Python-2.7.11/python+0x660980)
#18 0x65fc8a (/home/eminus/Downloads/Python-2.7.11/python+0x65fc8a)
#19 0x48e46c (/home/eminus/Downloads/Python-2.7.11/python+0x48e46c)
#20 0x7f4e72389ec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
#21 0x48c5bc (/home/eminus/Downloads/Python-2.7.11/python+0x48c5bc)

0x7f4e6ba64810 is located 16 bytes inside of 2097166-byte region [0x7f4e6ba64800,0x7f4e6bc6480e)
freed by thread T0 here:
#0 0x4766d3 (/home/eminus/Downloads/Python-2.7.11/python+0x4766d3)
#1 0x7f4e6f09b52d (/usr/lib/x86_64-linux-gnu/libtcl8.6.so+0x10e52d)

previously allocated by thread T0 here:
#0 0x4764d9 (/home/eminus/Downloads/Python-2.7.11/python+0x4764d9)
#1 0x7f4e6f09b0cc (/usr/lib/x86_64-linux-gnu/libtcl8.6.so+0x10e0cc)

SUMMARY: AddressSanitizer: heap-use-after-free ??:0 ??
Shadow bytes around the buggy address:
0x0fea4d7448b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fea4d7448c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fea4d7448d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fea4d7448e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fea4d7448f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0fea4d744900: fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fea4d744910: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fea4d744920: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fea4d744930: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fea4d744940: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fea4d744950: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
ASan internal: fe
==27988==ABORTING

PoC

from Tkinter import *

class Application(Frame):
    def say_hi(self):
        print ("hi there, everyone!")

    def createWidgets(self):
        self.QUIT = Button(self)
        self.QUIT["text"] = "QUIT"
        self.QUIT["fg"]   = "red"
        self.QUIT["command"] =  self.quit

        self.QUIT.pack({"side": "left"})

        self.hi_there = Button(self)
        self.hi_there["text"] = "Hello",
        self.hi_there["command"] = self.say_hi

        self.hi_there.pack({"side": "left"})


    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()
        self.createWidgets()

root = Tk(screenName=None, baseName=None, className='A'*0xfffff, useTk=1)
app = Application(master=root)
app.mainloop()
root.destroy()

@EminGhuliev EminGhuliev mannequin added type-security A security issue topic-tkinter labels Jun 7, 2016
@serhiy-storchaka
Copy link
Member

Minimal reproducer:

from tkinter import Tk
Tk(className='A'*0xfffff)

This looks as Tcl/Tk problem.

@serhiy-storchaka serhiy-storchaka added type-crash A hard crash of the interpreter, possibly with a core dump and removed type-security A security issue labels Jun 7, 2016
@EminGhuliev
Copy link
Mannequin Author

EminGhuliev mannequin commented Jun 8, 2016

Yeah you're right but Python doesn't check the classname length. Therefore then heap overflow occurred in the Tcl.

@serhiy-storchaka
Copy link
Member

What size is safe for className?

@EminGhuliev
Copy link
Mannequin Author

EminGhuliev mannequin commented Jun 8, 2016

the appropriate size should be chosen I)

@EminGhuliev
Copy link
Mannequin Author

EminGhuliev mannequin commented Jun 8, 2016

psuedocode

<+16>: movsxd rdx,DWORD PTR [rbx+0x8]
<+20>: lea eax,[rdx+rbp*1]

newSize = length ($rdx) + dsPtr->length ($rbp)
gdb > print /x $rbp
$5 = 0xfffff
gdb > print /x $rdx
$6 = 0x100000

newsize = 0xfffff+0x100000 = 0x1fffff

<Tcl_DStringAppend+23> cmp eax,DWORD PTR [rbx+0xc] ← $pc
<Tcl_DStringAppend+26> jl 0x7ffff6194e38 <Tcl_DStringAppend+104>

newSize ($eax) >= dsPtr->spaceAvl

gdb > print /x $eax
$7 = 0x1fffff

gdb > x/x $rbx+0xc
0x7fffffffd0cc: 0x001ffffe

condition: 0x1fffff >= 0x001ffffe = True

	if (newSize >= dsPtr->spaceAvl) {
		<Tcl_DStringAppend+31>  lea    esi,[rax+rax*1] ; magic compiler optimization :) (newSize(0x1fffff)*2)
		/*							*/
		dsPtr->spaceAvl = newSize * 2;
		gdb > print /x $rax
		$4 = 0x1fffff
		$esi = 0x1fffff+0x1fffff (newSize(0x1fffff)*2) = 0x3ffffe
		/*							*/
		
		=> <+34>:	lea    rax,[rbx+0x10]
		   <+38>:	mov    DWORD PTR [rbx+0xc],esi
		   <+41>:	cmp    rdi,rax ; $rax = dsPtr->staticSpace and $rdi = dsPtr->string
		   <+44>:	je     0x7ffff6194e50 <Tcl_DStringAppend+128>
		
		condition : dsPtr->string == dsPtr->staticSpace = False then jump to '<Tcl_DStringAppend+46>  call   0x7ffff60c2040 <Tcl_Realloc>'

	        if (dsPtr->string == dsPtr->staticSpace) {	          
			char *newString = ckalloc(dsPtr->spaceAvl);
            		memcpy(newString, dsPtr->string, (size_t) dsPtr->length);
			dsPtr->string = newString;
		} 
		else {
			<Tcl_DStringAppend+46>  call   0x7ffff60c2040 <Tcl_Realloc>
			$rsi = 0x3ffffe
			$rdi = 0x7ffff333e020
			dsPtr->string = ckrealloc(dsPtr->string = 0x7ffff333e020, dsPtr->spaceAvl = 0x3ffffe);
		}
	}

disassemble:
<Tcl_DStringAppend+58> lea rdi,[rax+rdx*1] ; dsPtr->string + dsPtr->length
<Tcl_DStringAppend+62> mov rsi,r12 ; bytes
<Tcl_DStringAppend+65> movsxd rdx,ebp ; length
<Tcl_DStringAppend+68> call 0x7ffff60a25c0 <memcpy@plt>
memcpy(dsPtr->string + dsPtr->length, bytes, length);

@EminGhuliev EminGhuliev mannequin changed the title heap overflow in Tkinter module UAF in Tkinter module Jun 12, 2016
@EminGhuliev EminGhuliev mannequin closed this as completed Jul 5, 2016
@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@chrstphrchvz
Copy link
Contributor

For reference, a Tcl ticket was opened for this issue: https://core.tcl-lang.org/tcl/info/d59715d0cffb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-tkinter type-crash A hard crash of the interpreter, possibly with a core dump
Projects
None yet
Development

No branches or pull requests

2 participants