ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Shallow Copy 문제점
    Hack/Pwnable 2018. 1. 29. 17:19

    Shallow Copy

    한국말로는 얕은 복사라고 하고, 반대의 복사방법은 Deep Copy, 깊은복사라고 한다.

    우선 아래 코드를 보자

    #include <iostream>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>

    using namespace std;

    class Person
    {
    public:
    int age;
    char *name;

    public:
    Person(int _age, char *_name) {
    age = _age;
    name = new char [strlen(_name)+1];
    strcpy(name,_name);
    }

    Person(const Person& s)
    {
    age = s.age;
    name = s.name;
    }

    ~Person() {
    // delete name;
    }

    void printPerson()
    {
    cout << "Name: " << name << endl;
    cout << "Age: " << age << endl;
    }
    };

    int main()
    {
    Person A(32,"s0ngsari");
    Person B = A;

    A.age = 21;
    strcpy(A.name, "tenten");

    A.printPerson();
    B.printPerson();
    }

    코드를 보면 Person 클래스안에 생성자, 소멸자, 멤버함수가 존재하는것을 알 수 있고, 메인함수에서는 해당 클래스 멤버에 값을 전달한다. A클래스 변수는 age 와 name 을 각각 32와 s0ngsari를 받고, B 클래스 객체는 그대로 받게된다. 그리고 A클래스 객체의 age와 name을 각각 21과 tenten으로 바꿔주고 출력을 시켜보았다. 먼저 실행결과는 아래와 같다.


    root@ubuntu:/home/study# ./shallow
    Name: tenten
    Age: 21
    Name: tenten
    Age: 32

    분명 A의 객체만 tenten으로 수정해주었는데 B의 name 또한 데이터가 바껴버렸다.

    Person 생성자에서 name을 new char로 힙에 할당을 해주는데, 힙 포인터를 같이 사용함을 알 수 있다.


    0x614c10: 0x00000000 0x00000000 0x00000021 0x00000000
    0x614c20: 0x676e3073 0x69726173 0x00000000 0x00000000

    실제로 A 클래스의 name과 B클래스의. name이 같은 주소를 가지고있는것을 확인할 수 있다.

    root@ubuntu:/home/study# ./shallow 
    0x1089c20
    0x1089c20
    Name: tenten
    Age: 21
    Name: tenten
    Age: 32

    이제, 위 코드에서 소멸자에 있는 delete name 주석을 지워보고 실행해보자. 당연히 더블프리버그가 발생할 것이다.


    (gdb) disas $rip
    Dump of assembler code for function _ZN6PersonD2Ev:
      0x0000000000400d40 <+0>: push   %rbp
      0x0000000000400d41 <+1>: mov   %rsp,%rbp
      0x0000000000400d44 <+4>: sub   $0x10,%rsp
      0x0000000000400d48 <+8>: mov   %rdi,-0x8(%rbp)
      0x0000000000400d4c <+12>: mov   -0x8(%rbp),%rax
      0x0000000000400d50 <+16>: mov   0x8(%rax),%rax
      0x0000000000400d54 <+20>: mov   %rax,%rdi
    => 0x0000000000400d57 <+23>: callq 0x400990 <_ZdlPv@plt>

    첫번째 A 소멸자에서 name을 delete하는데, B 객체도 name 가리키는 포인터가 같기 때문에 당연히 DFB가 발생한다.

    위 delete의 인자는 다음과 같다.


    (gdb) i r
    rax           0x614c20 6376480

    이는 name의 포인터인데, 당연히 터질수밖에 :)

    이런 버그를 방지하기 위해 개발자들은 깊은 복사를 이용한다.

    아래와 같이 깊은 복사를 이용해보자


    #include <iostream>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>

    using namespace std;

    class Person
    {
    public:
    int age;
    char *name;

    public:
    Person(int _age, char *_name) {
    age = _age;
    name = new char[strlen(_name)+1];
    strcpy(name,_name);
    }

    Person(const Person& s)
    {
    age = s.age;
    name = new char[strlen(_name)+1];
    strcpy(name,s.name);
    }

    ~Person() {
    delete name;
    }

    void printPerson()
    {
    cout << "Name: " << name << endl;
    cout << "Age: " << age << endl;
    }
    };

    int main()
    {
    Person A(32,"s0ngsari");
    Person B = A;

    A.age = 21;
    strcpy(A.name, "tenten");

    A.printPerson();
    B.printPerson();
    }

    얕은 복사와 같이 같은 포인터를 참조하고 값을 넣지않고, 새로운 영역을 할당해서 값을 관리하는것을 알 수 있다.

    root@ubuntu:/home/study# ./shallow 
    0x1711c20
    0x1711c40
    Name: tenten
    Age: 21
    Name: s0ngsari
    Age: 32

    A.name 과 B.name을 출력해보면 영역이 새로 할당되어 같은 포인터를 가리키지 않아, 버그가 발생하지 않는다.


    0x614c10: 0x00000000 0x00000000 0x00000021 0x00000000
    0x614c20: 0x746e6574 0x69006e65 0x00000000 0x00000000
    0x614c30: 0x00000000 0x00000000 0x00000021 0x00000000
    0x614c40: 0x676e3073 0x69726173 0x00000000 0x00000000
    0x614c50: 0x00000000 0x00000000 0x00000411 0x00000000

    소멸자가 호출될 때 또한 다른 포인터를 인자로 가진다.


    (gdb) i r $eax
    eax           0x614c40 6376512 // first destroy

    (gdb) i r $eax
    eax           0x614c20 6376480 // second destroy

    정답: 깊은 복사를 이용하자 ! 얕은 복사를 이용하면 Use-After-Free가 발생하여 함수포인터가 있다면 익스플로잇이 가능할것이당!


    'Hack > Pwnable' 카테고리의 다른 글

    Helper Python  (0) 2018.10.22
    ASIS CTF 2018 Tinypwn  (1) 2018.08.30
    File Stream Pointer에 관한 글  (6) 2017.01.24
    nc서버 열기 (XINETD)  (0) 2016.12.19
    malloc large_chunk exploit scenario  (2) 2016.12.02

    댓글

Designed by Tistory.