W tym poście opiszę sposób rozwiązania zadania PicoCTF (Capture The Flag link). Zadanie nazywa się Two Sum.
Zadanie polega na uzyskaniu flagi przez wykonanie przepełnia bufora w programie. Program uruchamiamy po zalogowaniu się na stronie.
Program działa w następujący sposób:
- n1 > n1 + n2 OR n2 > n1 + n2
- What two positive numbers can make this possible:
- 4000+50000
- You entered 4000 and 50000
- No overflow
Razem z programem działającym na serwerze otrzymujemy kod programu:
- #include <stdio.h>
- #include <stdlib.h>
- static int addIntOvf(int result, int a, int b) {
- result = a + b;
- if(a > 0 && b > 0 && result < 0)
- return -1;
- if(a < 0 && b < 0 && result > 0)
- return -1;
- return 0;
- }
- int main() {
- int num1, num2, sum;
- FILE *flag;
- char c;
- printf("n1 > n1 + n2 OR n2 > n1 + n2 \n");
- fflush(stdout);
- printf("What two positive numbers can make this possible: \n");
- fflush(stdout);
- if (scanf("%d", &num1) && scanf("%d", &num2)) {
- printf("You entered %d and %d\n", num1, num2);
- fflush(stdout);
- sum = num1 + num2;
- if (addIntOvf(sum, num1, num2) == 0) {
- printf("No overflow\n");
- fflush(stdout);
- exit(0);
- } else if (addIntOvf(sum, num1, num2) == -1) {
- printf("You have an integer overflow\n");
- fflush(stdout);
- }
- if (num1 > 0 || num2 > 0) {
- flag = fopen("flag.txt","r");
- if(flag == NULL){
- printf("flag not found: please run this on the server\n");
- fflush(stdout);
- exit(0);
- }
- char buf[60];
- fgets(buf, 59, flag);
- printf("YOUR FLAG IS: %s\n", buf);
- fflush(stdout);
- exit(0);
- }
- }
- return 0;
- }
Głównie interesuje nas funckja addIntOvf:
- static int addIntOvf(int result, int a, int b) {
- result = a + b;
- if(a > 0 && b > 0 && result < 0)
- return -1;
- if(a < 0 && b < 0 && result > 0)
- return -1;
- return 0;
- }
Potrzebujemy aby funkcja zwróciła wartość -1. Dzięki temu uda się odczytać flagę z serwera. Aby to wykonać wartość w zmiennej result musi przekroczyć maksymalną wartość dla zmiennej int czyli 2147483647.
Przepełnienie bufora polega na przekroczeniu maksymalnej wartości zmiennej. W przypadku int jest to zakres od -2147483648 do 2147483647 dla zmiennej zapisanej na 32 bitach. Więc dodając dwie liczby dodatnie, oczekujemy wyniku dodatniego. Jeśli nastąpi przepełnienie bufora to wchodzimy na wartości ujemne.
Dla przykładu:
- n1 > n1 + n2 OR n2 > n1 + n2
- What two positive numbers can make this possible:
- 2147483647+1000
- You entered 2147483647 and 1000
- sum: -2147482649
- //2147483647+1000−2^32=−2147482649
Wynik po przekroczeniu jest zawijany na część ujemną. Z tego powodu nie otrzymujemy poprawnego wyniku operacji arytmetycznej tylko wchodzimy w zakres liczb ujemnych.
Wobec tego wystarczy wprowadzić następujące dane:
- n1 > n1 + n2 OR n2 > n1 + n2
- What two positive numbers can make this possible:
- 2147483647+1
- You entered 2147483647 and 5000
- You have an integer overflow
- YOUR FLAG IS: picoCTF{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
W miejsca xxxx pojawi się poprawna flaga. Celowo ją tutaj zamazałem.
Program się nie wywala, ze względu na przepełnienie wartości integer, tylko wchodzi w odopowiednią instrukcję warunkową jeśli ono nastąpi. Natomiast warto się przyjrzeć jakie elementy w tym programie mogłyby zostać poprawione.
Jeśli tworzymy własny program, i chcemy zabezpieczyć go przed takimi zdarzeniami, należy wprowadzić sprawdzenie najlepiej przed rozpoczęciem operacji dodawania. W pierwszej wersji kodu dodawanie było robione dwa razy, bezpośrednio w funkcji addIntOvf oraz przed jej wywołaniem do zmiennej sum. Dodatkowo można usunąć przekazywanie wyniku operacji dodawania ze zmniennej addIntOvf.
- if (addIntOvf(num1, num2) == 0) {
- sum = num1 + num2;
- printf("No overflow\n");
- fflush(stdout);
- exit(0);
- }
Teraz funkcja addIntOvf mogła by wyglądać w następujący sposób:
- #include <limits.h>
- static int addIntOvf(int a, int b) {
- if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) {
- return -1; // Overflow detected
- }
- return 0; // No overflow
- }
Wobec tego poprawiony program mógłby wyglądać w następujący sposób:
- #include <stdio.h>
- #include <stdlib.h>
- #include <limits.h>
- static int addIntOvf(int a, int b) {
- if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) {
- return -1; // Overflow detected
- }
- return 0; // No overflow
- }
- int main() {
- int num1, num2, sum;
- FILE *flag;
- char c;
- printf("n1 > n1 + n2 OR n2 > n1 + n2 \n");
- fflush(stdout);
- printf("What two positive numbers can make this possible: \n");
- fflush(stdout);
- if(scanf("%d", &num1) != 1 || scanf("%d", &num2) != 1) {
- printf("Invalid input\n");
- fflush(stdout);
- exit(1);
- }
- else{
- printf("You entered %d and %d\n", num1, num2);
- fflush(stdout);
- int retVal = addIntOvf(num1, num2);
- if (retVal == 0) {
- sum = num1 + num2;
- printf("sum: %d\n", sum);
- printf("No overflow\n");
- fflush(stdout);
- exit(0);
- } else if (retVal == -1) {
- printf("You have an integer overflow\n");
- fflush(stdout);
- }
- if (num1 > 0 || num2 > 0) {
- flag = fopen("flag.txt","r");
- if(flag == NULL){
- printf("flag not found: please run this on the server\n");
- fflush(stdout);
- exit(1);
- }
- char buf[60];
- fgets(buf, sizeof(buf), flag);
- printf("YOUR FLAG IS: %s\n", buf);
- fflush(stdout);
- exit(0);
- }
- }
- return 0;
- }