diff options
Diffstat (limited to 'informationals/teso-i0022.txt')
| -rw-r--r-- | informationals/teso-i0022.txt | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/informationals/teso-i0022.txt b/informationals/teso-i0022.txt new file mode 100644 index 0000000..de5a592 --- /dev/null +++ b/informationals/teso-i0022.txt | |||
| @@ -0,0 +1,266 @@ | |||
| 1 | 0022 2000/03/19 TESO AUDIT summary: netkit-combo-0.16 | ||
| 2 | |||
| 3 | ==== TESO Informational ======================================================= | ||
| 4 | This piece of information is to be kept confidential. | ||
| 5 | =============================================================================== | ||
| 6 | |||
| 7 | Description ..........: TESO AUDIT summary: netkit-combo-0.16 | ||
| 8 | Date .................: 2000/03/19 22:00 | ||
| 9 | Author ...............: scut | ||
| 10 | Publicity level ......: unknown | ||
| 11 | Affected .............: Linux netkit-combo-0.16 | ||
| 12 | Type of entity .......: implementation | ||
| 13 | Type of discovery ....: auditing | ||
| 14 | Severity/Importance ..: medium | ||
| 15 | Found by .............: TESO AUDIT team | ||
| 16 | |||
| 17 | Information =================================================================== | ||
| 18 | |||
| 19 | This are the results from the TESO Audit project for the netkit-combo-0.16 | ||
| 20 | package. | ||
| 21 | |||
| 22 | No severe vulnerability has been found in the netkit package, although a lot | ||
| 23 | of minor issues and half-exploitable things were found. Altogether there were | ||
| 24 | seven issues found. | ||
| 25 | |||
| 26 | netkit-bootparamd | ||
| 27 | -- buffer overflow from config file parsing | ||
| 28 | description by Bawd: | ||
| 29 | |||
| 30 | Launching the bootparamd with the debug option in background | ||
| 31 | |||
| 32 | [root@foobar rpc.bootparamd]# ./bootparamd -d & | ||
| 33 | [2] 1268 | ||
| 34 | |||
| 35 | Now launch the callbootd (the bootparam debugging proggie given with | ||
| 36 | the package) | ||
| 37 | |||
| 38 | [root@foobar rpc.bootparamd]# ./callbootd 127.0.0.1 127.0.0.1 | ||
| 39 | bootparamd: whoami got question for 127.0.0.1 | ||
| 40 | This is host localhost | ||
| 41 | |||
| 42 | [2]+ Segmentation Fault (core dumped) ./bootparamd -d | ||
| 43 | |||
| 44 | Here it segfault, why ? | ||
| 45 | |||
| 46 | In fact, the bootparam daemon, receives our request of boot, it looks | ||
| 47 | at the file /etc/bootparams and copies the location of the bootparams | ||
| 48 | database. | ||
| 49 | |||
| 50 | Lets jump in the code to see what does the dameon exactly: | ||
| 51 | |||
| 52 | It's in the rpc.bootparamd.c and more precisely in: | ||
| 53 | |||
| 54 | static int getthefile(char *askname,char *fileid,char *buffer) | ||
| 55 | |||
| 56 | they say : | ||
| 57 | /* getthefile return 1 and fills the buffer with the information | ||
| 58 | of the file, e g "host:/export/root/client" if it can be found. | ||
| 59 | If the host is in the database, but the file is not, the buffer | ||
| 60 | will be empty. (This makes it possible to give the special | ||
| 61 | empty answer for the file "dump") */ | ||
| 62 | |||
| 63 | |||
| 64 | lets go: | ||
| 65 | bpf = fopen(bootpfile, "r"); | ||
| 66 | |||
| 67 | open the file /etc/bootparams | ||
| 68 | |||
| 69 | if (match) { | ||
| 70 | fid_len = strlen(fileid); | ||
| 71 | while (!res && (fscanf(bpf,"%s", info)) > 0) { | ||
| 72 | ch = getc(bpf); /* and a character */ | ||
| 73 | if (*info != '#') { /* Comment ? */ | ||
| 74 | if (!strncmp(info, fileid, fid_len) && | ||
| 75 | *(info + fid_len) == '=') | ||
| 76 | { | ||
| 77 | where = info + fid_len + 1; | ||
| 78 | if (isprint(*where)) { | ||
| 79 | /* found file */ | ||
| 80 | strcpy(buffer, where); | ||
| 81 | res = 1; break; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | |||
| 86 | the offending code is in the strcpy | ||
| 87 | it copies the line it found, into the buffer while the buffer is | ||
| 88 | defined like this: | ||
| 89 | (in the bootparamproc_getfile_1_svc) | ||
| 90 | |||
| 91 | if (getthefile(askname, getfile->file_id,buffer)) | ||
| 92 | |||
| 93 | and static char buffer[MAXLEN]; with #define MAXLEN 800 | ||
| 94 | |||
| 95 | so you can edit the /etc/bootparams configuration file and insert | ||
| 96 | something like | ||
| 97 | 192.168.0.111:/AAAAAAAAAAA*2000 | ||
| 98 | resulting in a segfault | ||
| 99 | |||
| 100 | So you see that we have to edit the configuration file. It cant be of | ||
| 101 | real use if you trying to remotely exploit this little bug, but (there | ||
| 102 | is a but), you can use it as a wicked backdoor. | ||
| 103 | Say you gained root access to the box and the box runs rpc.bootparamd. | ||
| 104 | Then you can edit the configuration file like this: | ||
| 105 | 156.3.2.1:THE_SHELLCODE_that_casts_a_shell | ||
| 106 | |||
| 107 | then remotely you can start callbootd like this: | ||
| 108 | ./callbootd <victim-ip> <156.3.2.1> | ||
| 109 | and here you have a good rpc.bootparamd that casts you a shell. | ||
| 110 | |||
| 111 | |||
| 112 | bsd-finger/finger/display.c:122 | ||
| 113 | -- documentation/commentation error | ||
| 114 | |||
| 115 | \t isn't stripped in finger replies, although comments say so. | ||
| 116 | |||
| 117 | * locale settings or is on the other side of the planet. So, | ||
| 118 | * strip 0-31, 127, 128-159, and 255. Note that not stripping | ||
| 119 | * 128-159 is asking for trouble, as 155 (M-esc) is interpreted | ||
| 120 | |||
| 121 | if (((ch&0x7f) >= 32 && (ch&0x7f) != 0x7f) || ch=='\t') { | ||
| 122 | |||
| 123 | |||
| 124 | bsd-finger/finger/display.c:141 | ||
| 125 | -- characters 127-159 that should be stripped can be circumvented | ||
| 126 | |||
| 127 | although characters above and equal to 0x80 are and'ed with \x7f they | ||
| 128 | are later outputted, prepended with "M-^<char>" the <char> is the old | ||
| 129 | original character and'ed with \x7f and \x40 added. so character \x9b, | ||
| 130 | which should be filtered can be outputted by using \xdb, since | ||
| 131 | (\xdb & \x7f) + '@' will result in \x9b. | ||
| 132 | |||
| 133 | if (ch&0x80) { | ||
| 134 | putc('M', f); | ||
| 135 | putc('-', f); | ||
| 136 | ch &= 0x7f; | ||
| 137 | } | ||
| 138 | |||
| 139 | putc('^', f); | ||
| 140 | if (ch==0x7f) putc('?', f); | ||
| 141 | else putc(ch+'@', f); | ||
| 142 | |||
| 143 | |||
| 144 | netkit-rwho/rwhod/rwhod.c:246 | ||
| 145 | netkit-rwho/rwhod/rwhod.c:294 | ||
| 146 | -- path relevant remotely supplied characters arent stripped from tempfile | ||
| 147 | |||
| 148 | rwhod creates a tempfile from the hostname passed from the remote | ||
| 149 | client: | ||
| 150 | |||
| 151 | wd.wd_hostname[sizeof(wd.wd_hostname)-1] = 0; | ||
| 152 | if (!verify(wd.wd_hostname)) { | ||
| 153 | syslog(LOG_WARNING, "malformed host name from %x", | ||
| 154 | from.sin_addr); | ||
| 155 | continue; | ||
| 156 | } | ||
| 157 | snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); | ||
| 158 | whod = open(path, O_WRONLY | O_CREAT, 0644); | ||
| 159 | |||
| 160 | while the wd_hostname is truncated and verified the verification | ||
| 161 | routine misses some important characters: | ||
| 162 | |||
| 163 | static int | ||
| 164 | verify(const char *name) | ||
| 165 | { | ||
| 166 | register int size = 0; | ||
| 167 | |||
| 168 | while (*name) { | ||
| 169 | if (!isascii(*name) || !(isalnum(*name) || | ||
| 170 | ispunct(*name))) | ||
| 171 | return (0); | ||
| 172 | name++, size++; | ||
| 173 | } | ||
| 174 | return size > 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | so verify ("../../") and verify ("/etc/") return true. whether this | ||
| 178 | results in something exploitable has to be checked. | ||
| 179 | |||
| 180 | Exploitation requires write access to /var/spool/rwho, which is 0755. | ||
| 181 | Unless there is a "arbitrary-make-directory" vulnerability this is | ||
| 182 | not exploitable. | ||
| 183 | |||
| 184 | |||
| 185 | netkit-ntalk/talkd/announce.c:135 | ||
| 186 | -- buffer being copied into another buffer may be 9 bytes too long | ||
| 187 | |||
| 188 | talkd creates some messages before writing the user supplied message | ||
| 189 | to the term. after doing this it merges all messages into one big | ||
| 190 | buffer. however the length checking can be circumvented, as in: | ||
| 191 | |||
| 192 | bptr = big_buf; | ||
| 193 | *bptr++ = '^G'; /* send something to wake them up */ | ||
| 194 | *bptr++ = '\r'; /* add a \r in case of raw mode */ | ||
| 195 | *bptr++ = '\n'; | ||
| 196 | for (i = 0; i < N_LINES; i++) { | ||
| 197 | /* copy the line into the big buffer */ | ||
| 198 | lptr = line_buf[i]; | ||
| 199 | while (*lptr != '\0') | ||
| 200 | *(bptr++) = safechar(*(lptr++)); | ||
| 201 | /* pad out the rest of the lines with blanks */ | ||
| 202 | for (j = sizes[i]; j < max_size + 2; j++) | ||
| 203 | *(bptr++) = ' '; | ||
| 204 | *(bptr++) = '\r'; /* add a \r in case of raw mode */ | ||
| 205 | *(bptr++) = '\n'; | ||
| 206 | } | ||
| 207 | *bptr = 0; | ||
| 208 | |||
| 209 | max_size can be (N_CHARS - 1) as largest value. then the total amount | ||
| 210 | of data being copied is (((N_CHARS - 1) + 2) * N_LINES) + 4 bytes long, | ||
| 211 | which is (N_CHARS = 120, N_LINES = 5): 121 * 5 + 4 = 609 bytes. the | ||
| 212 | buffer big_buf is only N_CHARS*N_LINES (= 600) bytes long. bingo. | ||
| 213 | |||
| 214 | this is only exploitable under a lot of conditions, practically this | ||
| 215 | is of no real use in the field. | ||
| 216 | |||
| 217 | |||
| 218 | netkit-ntalk/talkd/announce.c:161 | ||
| 219 | -- tty filename is user supplied opened without character white/blacklisting | ||
| 220 | |||
| 221 | the talk request announcement is made through directly writing at the | ||
| 222 | target users tty. to check whether this is possible the full path to | ||
| 223 | the tty device file is constructed. this construction does just | ||
| 224 | connects the dev directory path (usualy "/dev/") to the user supplied | ||
| 225 | tty. so user supplied terminal names such as "../root/.rhosts" is | ||
| 226 | possible theoretically. the bad thing about this is that previously | ||
| 227 | to this call this is blocked by the find_user function, which checks | ||
| 228 | whether a) the user is logged in, and b) the user supplied tty is | ||
| 229 | a correct one. however, it should be fixed. | ||
| 230 | |||
| 231 | snprintf(full_tty, sizeof(full_tty), "%s/%s", _PATH_DEV, | ||
| 232 | request->r_tty); | ||
| 233 | if (access(full_tty, F_OK) != 0) | ||
| 234 | return FAILED; | ||
| 235 | fd = open(full_tty, O_WRONLY|O_NOCTTY); | ||
| 236 | if (fd<0) { | ||
| 237 | return (PERMISSION_DENIED); | ||
| 238 | } | ||
| 239 | if (fstat(fd, &stbuf) < 0) { | ||
| 240 | return (PERMISSION_DENIED); | ||
| 241 | } | ||
| 242 | if ((stbuf.st_mode&020) == 0) { | ||
| 243 | return (PERMISSION_DENIED); | ||
| 244 | } | ||
| 245 | print_mesg(fd, request, remote_machine); | ||
| 246 | |||
| 247 | |||
| 248 | netkit-base/ping/ping.c:329 | ||
| 249 | -- invalid parsing of IP address information | ||
| 250 | |||
| 251 | if (sscanf(optarg, "%u.%u.%u.%u%c", | ||
| 252 | &i1, &i2, &i3, &i4, &junk) != 4) { | ||
| 253 | printf("bad interface address '%s'\n", | ||
| 254 | optarg); | ||
| 255 | exit(2); | ||
| 256 | } | ||
| 257 | ifaddr.s_addr = (i1<<24)|(i2<<16)|(i3<<8)|i4; | ||
| 258 | |||
| 259 | since the numbers are parsed as unsigned integers one can set an | ||
| 260 | arbitraty s_addr by using a defunc IP such as for example | ||
| 261 | 0.0.0.2378322822. this is not exploitable since later this address | ||
| 262 | is checked when setting the multicast options on the icmp socket. | ||
| 263 | however, it should be fixed. | ||
| 264 | |||
| 265 | =============================================================================== | ||
| 266 | |||
