How to query AWS WAF log for log4j attacks
SELECT *, unnested.labels.name FROM "my_db"."waf_logs" CROSS JOIN UNNEST(labels) UNNESTED (labels) where unnested.labels.name like '%Log4JRCE%'
SELECT *, unnested.labels.name FROM "my_db"."waf_logs" CROSS JOIN UNNEST(labels) UNNESTED (labels) where unnested.labels.name like '%Log4JRCE%'
python -m pip install --upgrade pip --trusted-host files.pythonhosted.org --trusted-host pypi.org
python -m venv some_name
./Scripts/activate
I am running this from Docker Desktop 2.3.0.2. on Windows 10. I am on my work network which brings special certificate issue.
Going to use this official Hashicorp Terraform Image.
##Pull down the latest version of terraform from Hashi FROM hashicorp/terraform:light ##Need this else you get cert trust error COPY "myWork.pem" "/usr/local/share/ca-certificates/" ##Need this to apply the new cert (above) on this box ##https://manpages.ubuntu.com/manpages/xenial/man8/update-ca-certificates.8.html#name RUN "/usr/sbin/update-ca-certificates" ##Need this so that it runs terraform upon launch ENTRYPOINT ["/bin/terraform"]
docker build -t terraform:latest .
docker image ls
docker run --rm -it terraform:latest -version
docker run --rm -it -e TF_LOG=%debugVar% -e TF_CLI_CONFIG_FILE=%TF_CLI_CONFIG_FILE_NEW% -v %cd%:/data -v %tf_config%:/terraform -w /data -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker:/var/lib/docker terraform:latest %newarg%
@echo off :dockerizedTerraform setlocal enabledelayedexpansion :: Initiall set this to 0, remember /A means this is number type set /A debug = 0 ::Set all the incoming argument into another variable, didn't know how to work with %* set "args=%*" ::loop through the arguments, when something we want to is found, flag it :: if there are more special flags we need to catch then just them here :: be sure to put quote around both side of comparison for %%x in (%*) do ( if "%%x" == "-debug" set /A debug = 1 ) :: If debug flag was set to 1 then remove -debug from the args if %debug%==1 ( set "newarg=%args:-debug= %" set "debugVar=DEBUG" ) else ( set "newarg=%args%" set "debugVar= " ) :: use -e for passing in environment variables to Docker container ::Need to pass in environment variable for the token file :: but we need to mount the volume and pass in the remote-end equivalent FOR %%i IN ("%TF_CLI_CONFIG_FILE%") DO ( :: get the folder path set "tf_config=%%~di%%~pi" :: get the file name and extension set "tf_config_file=%%~ni%%~xi" ) ::This will be the mount point for the terraform configuration file set "TF_CONFIG_PATH=terraform" ::THis will be the new config file location in the remote-end set "TF_CLI_CONFIG_FILE_NEW=/%TF_CONFIG_PATH%/%tf_config_file%" docker run --rm -it -e TF_LOG=%debugVar% -e TF_CLI_CONFIG_FILE=%TF_CLI_CONFIG_FILE_NEW% -v %cd%:/data -v %tf_config%:/terraform -w /data -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker:/var/lib/docker terraform:latest %newarg%
Be sure to work from Python Virtual Environment. Always work from venv. Follow this link. You can also see here.
1. Install Libraries that you want
python -m pip install requests
2. Show it's dependencies (you'll also see it being installed). You'll see Requires line with certifi, urllib3, charset-normalizer, idna
pip show requests
3. Open the folder /Lib/site-packages
4. Copy all the listed Requires packages into a separate directory called python
5. Zip up this folder. Resulting zip and content
locals{
layer_file = "${path.module}/requests_2_26_0.zip"
}
resource "aws_lambda_layer_version" "requests" {
filename = local.layer_file
layer_name = "requests"
compatible_runtimes = ["python3.8"]
source_code_hash = data.archive_file.requests.output_base64sha256
}
locals{
layer_file = "${path.module}/zips/new_layer.zip"
}
data "archive_file" "requests" {
type = "zip"
output_path = local.layer_file
source_dir = "${path.module}/layer_code"
output_file_mode = "0666"
}
resource "aws_lambda_layer_version" "new_layer" {
filename = local.layer_file
layer_name = "new_layer"
compatible_runtimes = ["python3.8"]
source_code_hash = data.archive_file.requests.output_base64sha256
}
In this guide, we configure Security Hub account to be able to take action on any other accounts in the Organization. This is all triggered from CloudWatch Event Pattern. The result looks like this.
resource "aws_cloudwatch_event_rule" "example" { name = "Example" description = "Example" event_pattern = <<EOF { "source" : [ "aws.securityhub" ], "detail-type" : [ "Security Hub Findings - Imported" ], "detail" : { "findings" : { "GeneratorId":["arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/4.3"], "Compliance":{ "Status":["FAILED"] } } } } EOF }
resource "aws_cloudwatch_event_target" "cloudwatch_event_target_sns" { count = local.sns_arn == null ? 0:1 ## [\.\-_A-Za-z0-9]+, 64 characters max rule = aws_cloudwatch_event_rule.cloudwatch_event_rule.name ## some unique assignment ID, random will be assigned since not provided ## target_id ## This is the ARN of the target regardless of the type arn = local.sns_arn }
Be sure the SNS topic permission include this
{
"Sid": "allow_from_events",
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": "SNS:Publish",
"Resource": "arn:aws:sns:us-east-1:99999999999:security-hub-topic"
}
resource "aws_cloudwatch_event_target" "cloudwatch_event_target_lambda" { count = local.lambda_arn == null ? 0:1 ## [\.\-_A-Za-z0-9]+, 64 characters max rule = aws_cloudwatch_event_rule.cloudwatch_event_rule.name ## some unique assignment ID, random will be assigned since not provided ## target_id ## This is the ARN of the target regardless of the type arn = local.lambda_arn }
Create the lambda function here
resource "aws_lambda_function" "default" { ## ([a-zA-Z0-9-_]+) function_name = local.rule_name filename = local.lambda_zip_file role = local.lambda_role_arn source_code_hash = local.lambda_hash description = "blah" handler = "lambda_function.lambda_handler" runtime = "python3.8" timeout = 60 memory_size = 128 }
data "aws_iam_policy_document" "lambda_role_basic" { ## Create log group statement { sid = "SidLogGroup" actions = ["logs:CreateLogGroup"] resources = ["arn:aws:logs:*:${local.self_account_id}:*"] } ## Create log stream statement { sid = "SidStream" actions = [ "logs:PutLogEvents", "logs:CreateLogStream" ] resources = ["arn:aws:logs:*:${local.self_account_id}:log-group:/aws/lambda/${var.rule_name_prefix}*:*"] } ## Allow this role to assume target roles in other accounts statement { sid = "SidAssumeAccountRoles" actions = ["sts:AssumeRole"] resources = ["arn:aws:iam::*:role/${var.target_role_name}"] } } resource "aws_iam_policy" "lambda_role_basic" { name = "${var.target_role_name}-policy" policy = data.aws_iam_policy_document.lambda_role_basic.json }
data "aws_iam_policy_document" "lambda_role_assume_policy" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["lambda.amazonaws.com"] } } }
resource "aws_iam_role" "lambda_role" { ## Create a new role and start with Assume Role Policy to let it be used by Lambda
## This is the role that is added to Lambda above name = var.lambda_role_name assume_role_policy = data.aws_iam_policy_document.lambda_role_assume_policy.json } resource "aws_iam_role_policy_attachment" "lambda_role_basic" { ## Attach the permission policy defined above role = aws_iam_role.lambda_role.name policy_arn = aws_iam_policy.lambda_role_basic.arn }
data "aws_iam_policy_document" "security_hub_assume_role_policy" { ## Allow the role from Main Account to assume this role statement { actions = ["sts:AssumeRole"] principals { type = "AWS" identifiers = ["arn:aws:iam::${local.sechub_account_id}:role/${local.security_hub_lambda_role_name}"] } } }
resource "aws_iam_role" "security_hub_role" { ## Create a new role, start with Assume Role Policy name = local.security_hub_role_name assume_role_policy = data.aws_iam_policy_document.security_hub_assume_role_policy.json }
resource "aws_iam_role_policy_attachment" "security_hub_read_policy" { ## Use built-in policy for this role = aws_iam_role.security_hub_role.id policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess" }
data "aws_iam_policy_document" "security_hub_edit_sechub_policy_doc" { statement { actions = [ "securityhub:CreateActionTarget", "securityhub:UpdateFindings", "securityhub:BatchDisableStandards", ] resources = ["arn:aws:securityhub:*:${local.account_id}:hub/default"] } } resource "aws_iam_policy" "security_hub_edit_sechub_policy" { name = "Security-hub-edit-sechub-policy" path = "/" policy = data.aws_iam_policy_document.security_hub_edit_sechub_policy_doc.json } resource "aws_iam_role_policy_attachment" "security_hub_edit_sechub_policy_attach" { role = aws_iam_role.security_hub_role.id policy_arn = aws_iam_policy.security_hub_edit_sechub_policy.arn }
data "aws_iam_policy_document" "security_hub_edit_IAM_policy_doc" { statement { actions = [ "IAM:DeleteAccessKey", "IAM:Detach*", "IAM:DeleteUserPolicy" ] resources = ["*"] } } resource "aws_iam_policy" "security_hub_edit_IAM_policy" { name = "Security-hub-edit-IAM-policy" path = "/" policy = data.aws_iam_policy_document.security_hub_edit_IAM_policy_doc.json } resource "aws_iam_role_policy_attachment" "security_hub_edit_IAM_policy_attach" { role = aws_iam_role.security_hub_role.id policy_arn = aws_iam_policy.security_hub_edit_IAM_policy.arn }
If you want to send alerts directly from EventBridge to SQS, you must modify your SQS to accept it from the Rule.
{
"Sid": "EventsToMyQueue",
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:region:account-id:queue-name",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "arn:aws:events:region:account-id:rule/rule-name"
}
}
}Reference: SQS Permissions.
This is fine if you know the arn of the rule in advance. However, if you require this SQS queue to receive messages from rules unknown and you want to limit who or what can send to this queue then you can't seem to use direct to SQS.
Because we're on an AWS Org, I tried this rule instead but unfortunately, services doesn't bring the necessary PrincipalOrgId information. Or I did it wrong...
{
"Sid": "doesnt_work",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:region:account-id:queue-name",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-9999999999"
}
}
}
Instead of sending directly to SQS, we can create a Lambda in conjunction with EventBridge rule and attach the necessary role with permission onto the Lambda function and then use PrincipalOrgId condition from SQS.
When you create your EventBridge and Lambda target, following permission needs to be attached to the Lambda.
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:region:account-id:function:function-name",
"Principal": {
"Service": "events.amazonaws.com"
},
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:events:region:account-id:rule/rule-name"
}
},
"Sid": "InvokeLambdaFunction"
}
Reference: Lambda Permissions.
And the IAM Role attached to the Lambda function needs to have (at least) this policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SidOrgLook",
"Effect": "Allow",
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:region:account-id:queue-name"
}
]
}
The lambda would have a function like this to send to queue:
def send_message(message,region): queue_name = os.environ.get("queue_name","generic_queue") sqs = boto3.resource('sqs', region_name = region) try: queue = sqs.get_queue_by_name(QueueName=queue_name) except: print("Error in fetching queue from name") return False try: response = queue.send_message(MessageBody=message) except: print("Error sending message") return False return response.get('MessageId')
Then the SQS itself has this Access Policy:
{
"Sid": "sqsQueueOrgSendPolicy",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"SQS:SendMessage"
],
"Resource": "arn:aws:sqs:region:account-id:queue-name",
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-9999999999"
}
}
Don't copy & paste policy into SQS Access Policy Web Console. I kept running into invalid JSON error. Do it using API call instead.
Go to Pipelines, Click New Pipeline
Select Azure Repos Git
Select Starter Pipeline
Clear content
Paste this
name: '$(BuildDefinitionName)_$(Build.BuildId)' trigger: branches: include: - master variables: major: '1' minor: '0' revision: $[counter(variables['minor'], 1)] MODULEVERSION: '$(major).$(minor).$(revision)'
In the assistant pane, search for Replace Tokens
In the assistant pane, search for Nuget (just regular Nuget) and enter as follows.
The resulting task:
Back to assistant pane, search again for Nuget
The resulting task:
That's it!
Save and run it.
With 0.13 Terraform is officially supporting Module For_Each Loop.
Given the below Subnet module
Subnet Module
variable "subnets"{ default = { "public" = { "0" = { "az" = 0 "cidr" = "10.1.0.0/18" } "1" = { "az" = 1 "cidr" = "10.1.64.0/18" } } "private" = { "0" = { "az" = 0 "cidr" = "10.1.128.0/19" } "1" = { "az" = 1 "cidr" = "10.1.160.0/19" } "2" = { "az" = 0 "cidr" = "10.1.192.0/19" } "3" = { "az" = 1 "cidr" = "10.1.224.0/19" } } } } locals { private_subnets = lookup(var.subnet_map,"private",{}) } resource "aws_subnet" "public_subnets" { for_each = local.public_subnets vpc_id = var.vpc_id cidr_block = each.value["cidr"] availability_zone = data.aws_availability_zones.az.names[each.value["az"]] map_public_ip_on_launch = "false" } output "public_subnets" { value = aws_subnet.public_subnets }
We can call it multiple times via this module call
variable "subnets_map"{ default = { "10.20.0.0/24" = { "public" = { "0" = { "az" = 0 "cidr" = "10.20.0.0/26" } "1" = { "az" = 1 "cidr" = "10.20.64.0/26" } } "private" = { "0" = { "az" = 0 "cidr" = "10.20.128.0/26" } "1" = { "az" = 1 "cidr" = "10.20.160.0/26" } } } "10.30.0.0/24" = { "public" = { "0" = { "az" = 0 "cidr" = "10.30.0.0/26" } "1" = { "az" = 1 "cidr" = "10.30.64.0/26" } } "private" = { "0" = { "az" = 0 "cidr" = "10.30.128.0/26" } "1" = { "az" = 1 "cidr" = "10.30.160.0/26" } } } } } module "many_subnets" { source = "../subnets" for_each = var.subnets_map vpc_id = var.vpc_id subnet_map = each.value } outputs "public_subnets"{ value = module.many_subnets.public_subnets }
The content of module.many_subnets looks like this:
module.many_subnets["10.30.0.0/24"] = private_subnets = { 0 = { arn = "arn:aws:ec2:us-east-1:xxxx:subnet/subnet-xxx" assign_ipv6_address_on_creation = false availability_zone = "us-east-1a" availability_zone_id = "use1-az2" cidr_block = "10.30.0.0/26" id = "subnet-xxxx" ipv6_cidr_block = "" ipv6_cidr_block_association_id = "" map_public_ip_on_launch = false outpost_arn = "" owner_id = "xxxx" tags = {} timeouts = null vpc_id = "vpc-xxx" } 1 = {...} }
After everything was done, I only wanted Subnet CIDR and ID, I could have gone back to the original module and updated the output, but I just did this local variable instead:
private_subnets = {
for key, value in flatten([
for item in module.many_subnets : [
for child in item.private_subnets : {
"id" = child.id
"cidr" = child.cidr_block
}
]
]) : (value["cidr"]) => (value["id"])
}
https://pkgs.dev.azure.com/ORG/PROJECT/_packaging/FEED/nuget/v3/index.json
mkdir c:\mudNew-ModuleManifest -Path .\mud.psd1
New-item .\mud.psm1
RootModule = 'mud.psm1' ModuleVersion = '1.1.0' FunctionsToExport = @('function1','function2') FileList = @('file1.ps1','file2.ps1')
nuget spec mud
nuget pack mud.nuspec
nuget sources Add -Name "myfeed" -Source $source_push -username $username -password $pat
nuget push -Source "myfeed" -ApiKey AzureDevOpsServices .\mud.nupkg
> Register-PSRepository -Name $repo_name -SourceLocation $source_repo -InstallationPolicy Trusted > Get-PSRepository > find-module -Repository $repo_name -credential $credsAzureDevopsServices > Install-Module -Name $module_name -Repository $repo_name -credential $credsAzureDevopsServices
module "testmodule" { source = "app.terraform.io/BLAH/testmodue/aws" version = "2.0.0" }
Create venv
> py -m venv tfc_env
Activate it
\tfc_env\Scripts\activate Deactivate it
> deactivate
## You need to activate this virtual env before you run this: ##> .\python\Scripts\activate ## When you are done, you should deactivate it: ##> deactivate import os ## Doc for this is here https://github.com/adeo/iwc-tfc-client ## pip install tfc_client --trusted-host pypi.org --trusted-host files.pythonhosted.org from tfc_client import TFCClient from tfc_client.enums import ( RunStatus, NotificationTrigger, NotificationsDestinationType, ) from tfc_client.models import VCSRepoModel ##Needed this Self-Signed Cert when working on VPN os.environ["REQUESTS_CA_BUNDLE"]="./mycert.pem" #$env:REQUESTS_CA_BUNDLE="./mycert.pem" # Instanciate the client ## Get the token from web console and paste it into the file token = open("token.txt", "r").read() client = TFCClient(token=token) # Retreive any object type by ID from the client my_org = client.get("organization", id="xxxxxxxx") my_ws_byID = client.get("workspace", id="ws-111111111") my_ws_byName = my_org.workspace(name="7777777777") my_run = client.get("run", id="run-777777777777777") my_var = client.get("var", id="test") # To retreive all workspaces: for ws in my_org.workspaces: print(ws.name) print(my_run) print(my_var) #my_run = my_ws_byName.create("run", message="Run run run")
output "example"{ value = cidrsubnets("10.1.0.0/16",1,1) }
example = [ "10.1.0.0/17", "10.1.128.0/17", ]
0.0 to 127.255 <- possible range 00000000.00000000 <- network 10000000.00000000 <- mask 128.0 to 128.255 10000000.00000000 10000000.00000000
Error: Invalid function argument on main.tf line 50, in output "example": 50: value = cidrsubnets("10.1.0.0/16",1,1,1) Invalid value for "newbits" parameter: not enough remaining address space for a subnet with a prefix of 17 bits after 10.1.128.0/17.
output "example"{ value = cidrsubnets("10.1.0.0/16",2,2,2,2) } example = [ "10.1.0.0/18", "10.1.64.0/18", "10.1.128.0/18", "10.1.192.0/18", ] ========================== 0.0 to 63.255 00000000.00000000 11000000.00000000 64.0 to 127.255 01000000.00000000 11000000.00000000 128.0 to 191.255 10000000.00000000 11000000.00000000 192.0 to 255.255 11000000.00000000 11000000.00000000
output "example"{ value = cidrsubnet("10.1.0.0/16",2,3) } example = 10.1.192.0/18
for (i=0; i<5; i++){ }
locals{ counter_map={ 1=[0], 2=[0,1], 3=[0,1,2], 4=[0,1,2,3], 5=[0,1,2,3,4] } private_subnets = [for item in local.counter_map[var.private_count]: cidrsubnet(local.subnets[1], local.private_count_newbit, item)] }
locals{ counter_set_all = [0,1,2,3,4,5,6,7,8,9] public_subnets = [for item in [for item in local.counter_set_all : item if item < var.public_count]: cidrsubnet(local.subnets[0], local.public_count_newbit, item)]}
variable "public_count_weight"{ default = 1 description = "newbit weight given to subnet, lower means bigger subnet" } variable "private_count_weight"{ default = 1 description = "newbit weight given to subnet, lower means bigger subnet" } variable "private_count"{ default = 2 } variable "public_count"{ default = 2 } locals{ ## newbits can be calculated from the number of subnets desired by looking at the binary log to the desired subnet ## If 2 subnets are required, this requires 1 more bit in the netmask (2^1 = 2 or log base 2 of 2 = 1) ## If 4 subnets are required, this requires 2 more bits in the netmask (2^2 = 4 or log base 2 of 4 = 2) ## If 8 subnets are required, this requires 3 more bits in the netmask (2^3 = 8 or log base 2 of 8 = 3) public_count_newbit = ceil(log(var.public_count , 2 )) private_count_newbit = ceil(log(var.private_count , 2 )) ## split the initial CIDR into two, one for public and one for private subnets = cidrsubnets("10.1.0.0/16", var.public_count_weight, var.private_count_weight) ## Split each of the half from above for desired number of subnet in each type public_subnets = var.public_count == 1 ? [local.subnets[0]]:[for item in [for item in local.counter_set_all : item if item < var.public_count]: cidrsubnet(local.subnets[0], local.public_count_newbit, item)] private_subnets = var.private_count == 1 ? [local.subnets[1]]:[for item in local.counter_map[var.private_count]: cidrsubnet(local.subnets[1], local.private_count_newbit, item)] } locals{ counter_map={ 1=[0], 2=[0,1], 3=[0,1,2], 4=[0,1,2,3], 5=[0,1,2,3,4] } counter_set_all = [0,1,2,3,4,5,6,7,8,9] } output "subnets" { value = local.subnets } output "private"{ value = local.private_subnets } output "public"{ value = local.public_subnets }
How to query AWS WAF log for log4j attacks 1. Setup your Athena table using this instruction https://docs.aws.amazon.com/athena/latest/ug/wa...