Vulnerability Development mailing list archives

Re: Telnetd AYT overflow scanner and linux telnet 0.17


From: H D Moore <hdm () secureaustin com>
Date: Fri, 3 Aug 2001 23:15:06 -0500

I have been educated.  Comments are inline:

On Fri, 3 Aug 2001 07:25:40 +1200 (NZST)
<zen-parse () gmx net> wrote:

bash-2.04$ objdump -t /usr/sbin/in.telnetd|grep netobuf
080585a0 g     O .bss   00002040              netobuf

As you can see, netobuf is in the .bss segment. (NOT THE STACK)

Which explains why eip/esp was never any part of the response...

A good hint that something might be on the stack is the list of variables
declared in the function. Local variables are on the stack. For example.
len and maxsize are probably on the stack in this function...

And this explains why I couldn't find any of the local variables...

(Multiples of 4 are also more likely to be the right length (More likely
only though.)) An int is length 4bytes. (I have no idea where you got 30
from.) You didn't give any addresses in the posting that I could use
to verify you knew what you were talking about when you said that gdb
might've been allocating 30 bytes per int, so I'm going to assume you
didn't.

Looking at the memory with gdb (starting at pcc) shows this:

(gdb) x 0x<address of pcc>\n
0x<addr> <pcc>: 0x0000
(gdb) \n
0x<addr+2> <pcc + 2>: 0x0000
(gdb) \n
0x<addr+4> <pcc + 4>: 0x0000
[ ... ]
(gdb) \n
0x<addr+30> <pcc + 30>: 0x0000

Does that mean that 30 bytes are allocated for the integer pcc, or that gdb
has no idea what the memory is after the variable's base address? The
netoprintf walk went all the way up to netoprintf+8256, which happened to 
be the correct size for it, I assumed that other variables were treated the
same way.  If everything after pcc+4 isn't something allocated by gcc for 
that int variable, what is in that space? Examining the memory before the
overflow shows it all zero'd out.

(I say probably, because depending on optimizations in compiling, they may
be stored in registers, and not even [be able to be touched with / need
to be counted in calculatning the size of] a stack overflow.)

Does register optimization only apply to numeric variables?  What about
pointers and single char's?

I think you maths is slightly out. 8192 + 64 = 8256 and its unlikely that
any form of optimization or rearrangement so its able to be traced more
efficienlty would result in a buffer that is 2 bytes smaller than it has
been declared.

That was a typo, I had 8256 in my notes. The mod 4 is a good tip though.

A normal (4000 ayt's) only seems to get about halfway into subbuffer
before the program exits.

Could it be you were sending them in 'packets'? If the daemon does a
netflush() between parts then they start going back from the beginning of
the buffer.

Hrm.  They were sent as a single packet, I need to look at the buffer length
passed to the read() call and see if I went over.

The 2nd thing you said initially that made me write this email.
 You can't control the data.

You can control the data.
Can you show me the rule that says you are only allowed to use AYT to
explot this bug?

No ;)

Have a look on the internet for RFCs (Request For Comments) dealing with
the TELNET protocol.

You will see there is a great deal of things you can do.

I didn't get far enough into the protocol to figure out the right order. Just
sending a few thousand can_do/will_do's didn't crash it, but I probably needed to set
something else up first.  I spent some time with a sniffer and the telnet client's
^]send functions trying to determine the packet order and possible paths through
the state engine in state.c, obivously I didn't get far enough before I sent this
out, just reading the RFC would have been easier I guess.

Also looking at the source (did you?) will should you there are more
replies that get put into the netobuf (via the variable nfrontp, which
I'm sure you would've noticed is initialized to be equal to netobuf.)
You also would've seen the send_will() send_wont() send_do() and send_dont()
functions, and noticed that they don't do any bounds checking either,
trusting that if you could send it in one message, it could be stored in a
buffer the same size. You would also notice that you can control the last
byte of each 3 byte cluster? And noticed that a \0 gets added to the end
of the buffer under most situations, either by the vsnprintf() or by
another function?

The last byte is the option the client requests?  I will definately take a
closer look here, the rather crude tests I did weren't having any effect.

Read the RFCs there is even one quite funny one, if you look hard enough,
that should entertain you enough to continue looking through them enough
to work out how to do this properly.

(strlen(hostname) + 12) * ayt

Yoy! Something that is accurate!

Woohoo, I get 1 point!

Alright what do we have.  We can make a string which is

(strlen(hostname)+12) *N + 3 *M bytes long by sending a string that is
2 * N + 3 * N bytes long. Now, bear with me for a momement.

In an ideal world (which this isn't, but we will pretend it is)
two-thirds of computer names will not be a multiple of 3 letters long.

Huh? you go.

Well, if you are slightly lucky, and have a machine that returns a name
that is not a multiple of 3 bytes long, you can overflow ANY distance (up
2 the maximum imposed by the input length of course)  distance into the
heap.

'Huh?' you go.

(.bss segment is 'low heap', followed by the area that get malloc()ed for
things, the heap, proper.)
To exploit this, you would need to use the AYT overflow to overwrite one
of the internal stack variables (and create a secondary vulnerability),
                  ^^^^^ <- not stack!!!!!        ^^^ YAY
then exploit this newly created vulnerability to launch a shell. )
                                                            ^^^^^^<-no!!!!

In a few days, maybe a week, all will become clear.

Cool, looking forward to seeing it, thanks again for the explanation. Hopefully
I didn't cause too much damage with my ignorant overview of the problem. 
 
-- zen-parse

-HD


Current thread: