euid, ruid, suid

htARTE(HackTricks AWS Red Team Expert)を使って、ゼロからヒーローまでAWSハッキングを学びましょう!

ユーザー識別変数

  • ruid: 実ユーザーIDはプロセスを開始したユーザーを示します。

  • euid: 有効ユーザーIDとして知られ、システムがプロセス特権を確定するために使用するユーザーIDを表します。一般的に、euidruidと同じであり、SetUIDバイナリの実行などの場合を除いて、euidはファイル所有者のIDを取り、特定の操作権限を付与します。

  • suid: この保存されたユーザーIDは、一時的に特定のタスクを実行するために高特権プロセス(通常はrootとして実行)が特権を一時的に放棄する必要がある場合に重要です。後で元の昇格された状態を取り戻します。

重要な注意事項

rootで動作していないプロセスは、現在のruideuid、またはsuidに一致するようにeuidを変更できます。

set*uid関数の理解

  • setuid: 最初の仮定とは異なり、setuidは主にruidではなくeuidを変更します。特権プロセスの場合、setuidruideuid、およびsuidを指定されたユーザー(通常はroot)と一致させ、これらのIDを効果的に固定します。詳細な情報はsetuid man pageにあります。

  • setreuidおよびsetresuid: これらの関数はruideuid、およびsuidを微調整することを可能にします。ただし、その機能はプロセスの特権レベルに依存します。rootプロセスまたはCAP_SETUID機能を持つプロセスは、これらのIDに任意の値を割り当てることができます。詳細はsetresuid man pageおよびsetreuid man pageから得ることができます。

これらの機能はセキュリティメカニズムではなく、プログラムが他のユーザーのIDを採用する際など、意図した操作フローを容易にするために設計されています。

setuidはrootへの特権昇格に一般的に使用されるかもしれませんが、これらの関数の違いを区別することは、さまざまなシナリオでユーザーIDの動作を理解し操作するために重要です。

Linuxでのプログラム実行メカニズム

execveシステムコール

  • 機能: execveは最初の引数で決定されたプログラムを開始します。argvは引数用、envpは環境用の2つの配列引数を取ります。

  • 動作: 呼び出し元のメモリ空間を保持しますが、スタック、ヒープ、およびデータセグメントをリフレッシュします。プログラムのコードは新しいプログラムに置き換えられます。

  • ユーザーIDの保持:

  • ruideuid、および補助グループIDは変更されません。

  • 新しいプログラムがSetUIDビットを設定している場合、euidに微妙な変更が加えられる可能性があります。

  • 実行後、suideuidから更新されます。

  • ドキュメント: 詳細な情報はexecve man pageにあります。

system関数

  • 機能: execveとは異なり、systemforkを使用して子プロセスを作成し、その子プロセス内でexeclを使用してコマンドを実行します。

  • コマンドの実行: execl("/bin/sh", "sh", "-c", command, (char *) NULL);を使用してshを介してコマンドを実行します。

  • 動作: execlexecveの形式であるため、新しい子プロセスのコンテキストで同様に動作します。

  • ドキュメント: system man pageからさらなる洞察を得ることができます。

SUIDを持つbashおよびshの動作

  • bash:

  • -pオプションがeuidruidの扱いに影響を与えます。

  • -pなしでは、bashは最初に異なる場合にeuidruidに設定します。

  • -pを使用すると、初期のeuidが保持されます。

  • 詳細はbash man pageで確認できます。

  • sh:

  • bash-pに類似したメカニズムを持っていません。

  • ユーザーIDに関する動作は明示的に述べられておらず、-iオプションの下でのみeuidruidの等しさの保持が強調されています。

  • 追加情報はsh man pageで入手できます。

これらの操作方法は、操作とプログラム間の移行を可能にし、ユーザーIDがどのように管理および保持されるかに特定の微妙な違いがある、多様なオプションを提供します。

実行中のユーザーIDの動作をテストする

詳細については、https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail から取得した例を確認してください。

ケース1: systembashshと組み合わせてsetuidを使用する

目的: setuidsystemおよびbashとしてshと組み合わせて使用した場合の効果を理解する。

Cコード:

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>

int main(void) {
setuid(1000);
system("id");
return 0;
}

コンパイルと権限:

oxdf@hacky$ gcc a.c -o /mnt/nfsshare/a;
oxdf@hacky$ chmod 4755 /mnt/nfsshare/a
bash-4.2$ $ ./a
uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0

分析:

  • ruideuid は最初にそれぞれ99(nobody)と1000(frank)から始まります。

  • setuid は両方を1000に揃えます。

  • system/bin/bash -c id を実行します。これはshからbashへのシンボリックリンクによるものです。

  • -p を付けずに実行される bash は、euidruid に合わせ、結果として両方が99(nobody)になります。

ケース2: setreuidをsystemと一緒に使用する

Cコード:

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>

int main(void) {
setreuid(1000, 1000);
system("id");
return 0;
}

コンパイルと権限:

oxdf@hacky$ gcc b.c -o /mnt/nfsshare/b; chmod 4755 /mnt/nfsshare/b

実行と結果:

bash-4.2$ $ ./b
uid=1000(frank) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0

分析:

  • setreuid は ruid と euid を両方とも 1000 に設定します。

  • system は bash を呼び出し、ユーザー ID を等しく保持するため、実質的に frank として動作します。

ケース 3: execve と setuid の相互作用の探索

目的: setuid と execve の相互作用を探る

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>

int main(void) {
setuid(1000);
execve("/usr/bin/id", NULL, NULL);
return 0;
}

実行と結果:

bash-4.2$ $ ./c
uid=99(nobody) gid=99(nobody) euid=1000(frank) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0

分析:

  • ruid は99のままですが、euid は1000に設定され、setuid の効果と一致しています。

Cコード例2(Bashの呼び出し):

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>

int main(void) {
setuid(1000);
execve("/bin/bash", NULL, NULL);
return 0;
}

実行と結果:

bash-4.2$ $ ./d
bash-4.2$ $ id
uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0

分析:

  • euidsetuid によって1000に設定されていますが、-p がないため、bashruid (99) に euid をリセットします。

Cコード例3 (bash -p を使用):

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>

int main(void) {
char *const paramList[10] = {"/bin/bash", "-p", NULL};
setuid(1000);
execve(paramList[0], paramList, NULL);
return 0;
}

実行と結果:

bash-4.2$ $ ./e
bash-4.2$ $ id
uid=99(nobody) gid=99(nobody) euid=100

参考文献

ゼロからヒーローまでのAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)

Last updated