Security work / NYU coursework / AppSec labs

Learning AppSec by breaking things.

My NYU application security course was a lot of time spent inside deliberately vulnerable code, figuring out why something crashed, why an exploit worked, and why the first fix sometimes wasn't enough. These are notes from what I actually did, cleaned up enough to be readable but not rewritten into a perfect success story.

Academic courseworkC + PythonDjangoDocker + KubernetesAndroid / Kotlin
The setup

From C bugs to mobile app permissions.

The assignments moved from a C gift-card reader to a Django website, then into containers and Kubernetes, and finally into an Android client. Most of the code was intentionally insecure. The point was to find the problem, make it reproducible, understand why it happened, and change the implementation.

I still have my original lab notes, including the dead ends and things that didn't pass the autograder even when they seemed to work locally. I'm keeping that distinction here. This is coursework, not a description of production security work.

01 / C, fuzzing and memory safety

I thought a malformed file would be simple. It wasn't.

We started with a C program that parsed a legacy gift-card file. I made malformed inputs to trigger segmentation faults and an infinite loop. One crash came from changing bytes where a merchant name should have been; the animation interpreter was another place where moving the program counter backward could keep the loop running.

My notes are full of attempts: changing the file generator, poking at hex values, trying input lengths, and then going back into the reader and debugger. I used gdb, coverage reports, and fuzzing to get beyond ordinary test inputs. I added regression cases to the test suite and used GitHub Actions for build/test automation.

What I would say differently now

Some of my early fixes were very narrow, like exiting when one specific bad value appeared. My own notes also contain a mistaken sizeof(argv) check; the argument count should be checked using argc. Re-reading this, I care more about validating lengths, ownership, and pointer boundaries systematically than just stopping the crash I happened to reproduce.

C reader, test cases, and original notes →

02 / Django and web exploitation

The exploit made more sense once I followed the request.

We audited a gift-card site for XSS, CSRF, SQL injection, and command injection. I spent a lot of time in Django views and templates looking at how GET and POST requests moved through the application.

XSS

I traced an unsafe |safe template filter to attacker-controlled content, reproduced script execution in the lab, and removed the filter to restore escaping.

CSRF

I got a cross-site gift-card transfer to work locally, then worked through Django's CSRF middleware and form tokens. I noted that the submitted test did not pass the autograder.

SQL injection

I found a query built by string interpolation and tried replacing it with a parameterized call. I also wrote down that my patch appeared to work locally but did not pass grading.

Command injection

I traced unsafe filename handling into command execution and added restrictive input handling in the lab. Today I'd also want to remove unnecessary shell interpretation at the boundary.

I remember getting stuck on CSRF because I initially tried to put the entire attack in a browser URL. Office hours helped me understand the difference between navigating to a URL and getting a browser to submit a form on someone's behalf.

The lesson was less “remember to sanitize input” and more know which layer is supposed to protect which operation: template escaping, CSRF validation, parameter binding, and avoiding command shells solve different problems.

Django lab and original notes →

03 / Containers, Kubernetes and monitoring

Then we had to secure the deployment.

Next we worked with a Dockerized Django app, MySQL database, Nginx reverse proxy, and Kubernetes manifests. The exercise included credentials placed in code or configuration, sensitive data exposed through metrics, and incomplete monitoring.

I worked on separating secrets from the application configuration and using Kubernetes secrets and environment variables. My Prometheus notes describe removing a counter that exposed passwords in metric labels, adding a count for database-error 404 responses, installing Prometheus with Helm, and configuring it to scrape the application through the proxy service.

That exercise connected two things I care about: observability is necessary, but collecting something because you can is not the same as collecting something you should. Credentials don't belong in metrics any more than they belong in source control.

My setup notes also show some unresolved service-address troubleshooting. I don't want to pretend the Kubernetes side was effortless or that every monitoring configuration was validated perfectly.

Container and monitoring lab →

04 / Android security and privacy

Who can launch a component, and what can the app read?

The Android assignment made me think about security as part of the operating system's interaction model. I reviewed explicit versus implicit intents and changed activity exposure so unrelated apps couldn't launch internal screens unnecessarily. I also replaced cleartext HTTP calls with HTTPS and set usesCleartextTraffic to false.

I removed unnecessary location and network-state permissions and looked at sensor and location reporting code that had no good reason to exist in a gift-card app. My notes show the permissions cleanup passing its check while the sensor-interaction cleanup did not fully pass. That's a useful distinction: removing a permission declaration is not the same as removing every data-collection code path.

I also flagged a token construction and API authorization concern. That was analysis in a lab, not evidence of a production OAuth implementation.

Android lab and my write-up →

What stayed with me

What I remember from the course.

These labs were messy in the way actual debugging is messy. A file crashed for reasons I didn't expect. A local fix didn't always satisfy a test harness. The behavior of a browser, a server, a container, and a phone each added another boundary to understand.

What I took from the course is a way to approach security work: reproduce the failure, find the underlying trust assumption, use the framework or platform protection designed for it, and write a test that can catch the regression. I use that same lens now when I review software I've built myself.

Source and scope

Based on my own lab notes and coursework repository. The exercises deliberately include vulnerable code, attack test cases, and demonstration configurations. They are educational artifacts, not deployable templates. Some notes describe partial solutions or grading mismatches.

See that thinking applied to Pet Sticker Maker →