sudoの脆弱性 CVE-2019-14287 をcode levelで追ってみる 2019年10月16日


注意書き

あくまでも個人が,研究の息抜きに趣味程度に調べたことである.隅々まで調べたわけではないため誤りを含んでいる可能性がある.(間違いを見つけたらこっそり教えて下さい)鵜呑みにしないこと.検証すること.

体系的に記述しておらず,また飛び飛びで分かりづらいのは申し訳ない.

TL; DR;

  • 2019年10月14日に公開された,sudoの脆弱性CVE-2019-14287をコードレベルで追ってみた
  • 公開されている情報のとおり,uidが-1かどうかのcheckをせず,setresuidに渡しているため,sudoersの制約( “!root”; rootとしての実行を禁止) を回避しrootとして実行できていた
  • patch自体はrepoではすでにできている様子なので,そのうち各Linuxのpackage managerでも入るようになると思われる
    • patchを待てない人は,sudoersにおいて 次のような,root以外の人のユーザでコマンドが実行できる,といった記述がある場合,特定のユーザの権限としてのみ実行可能,と言った記述などに修正する(可能なら)
    • x user1 ALL=(ALL, !root) /path/to/cmd
    • o user1 ALL=(user2) /path/to/cmd

どのような脆弱性か

JPCERTでの注意喚起にもあるとおり,

sudoersにおいて,あるユーザに対し,「root以外の任意のユーザでコマンドを実行可能」と記述されている場合において,「root以外の」という制約を回避することができる脆弱性,である.

JPCERTの例ではあるコマンドとしているが,もちろん以下のようにすべてのコマンドを実行可能としてもよい.この例では,user “test”に対し,任意のコマンドをroot以外の任意のユーザで実行可能としている.

test ALL=(ALL, !root) ALL

(このようなruleがどのような条件下で設定されるのか,という点で懐疑的であるため実例が知りたい)

この条件下において,以下のように脆弱性が再現できる.

$ id
uid=1001(test) gid=1001(test) groups=1001(test) 
$ sudo -u root id
[sudo] password for test: 
Sorry, user test is not allowed to execute '/bin/id' as root on localhost
$ sudo -u#-1 id
[sudo] password for test: 
uid=0(root) gid=1001(test) groups=1001(test) 

だれが影響を受るのか

JPCERTのページでも記述されている通り,また上でも記述したとおり,

「root意外の任意のユーザとしてコマンドの実行を許可」

するようなsudoersになっていれば影響を受ける.一方で,そうでない人や,パッチがあたっている人は影響を受けない.おおよそ,ほとんどの人には無関係な脆弱性のはずである(未確認).

patchを確認する

commit logを追う限り,次の修正が本脆弱性に対するpatchであるように見える.

https://github.com/sudo-project/sudo/commit/f752ae5cee163253730ff7cdf293e34a91aa5520

id_tは環境に応じてlongまたはlong longとなるようで,環境に応じて修正が異なる.(再現環境のCentOS 8, x86_64 ではlongであった.)

なお,最新のcommitにおいては,id_tがlongかlong longかのmacroが消失している.

この修正で,-1を入力することができなくなった. また,id_tがlongな環境に置いては,errno=ERANGEを見るようになり,4294967295を含めることもできなくなった.

脆弱性が起きる流れ

ざっくりいうと,uidに-1を指定した状態でsudoすると,setresuidで権限をsetする際,-1がuidにそのまま渡ることに起因する.

-1をuidに渡した場合,uidは変更しない,という処理になることから,rootのまま実行される.(sudoにはsetuid bitが立っているので,sudoそのものはrootとして実行される)

なお,正確にはsetresuidのuidの方はuid_tであるので,実際の値は4294967295である.

後述するが,ここのようにid_t -> uid_tのcastをしていることから,id_tがlongな環境に置いては,-1はcastされて4294967295になる.(-1でも4294967295でもいいのは,このcastによる.)

-u#-1が渡されたときのざっくりとした内部処理

ダラダラ書きすぎた,すみません… / 処理の概要は本記事には書いておらず,下記の記述は部分部分の記述のため,codeを読みながら見たほうがよいと思われrる.これらはcommit id : fd5d0f511efa009cd93edcdabc693f839818daadを元に作成している.

所感

  • 流し読み程度にsudoの実装を読んでみたが思っていたよりもカオスだった
  • 脆弱性そのものについては,原因はまぁそういうことですよね,という感じで大方予想からは外れていなかった
    • とはいえcode読むのは楽しい
    • ガッツリ読んだわけではないので,codeの意図は深くは捉えられていない
  • 上記にも書いたが, (ALL, !root) を書かなければならないケースが思いつかない
    • みんな割と騒いでいるように見えるので,僕が知らないだけで実はそういうケースに満ちあふれているのだろうか...?
  • id_tがlong longな環境って何があるんだろう...

P.S.

先週誕生日でしたのでほしいものリストを置いておきますね.

Leave a Reply